1 Introduction

Machine learning (ML) has revolutionized various industries by enabling the development of systems capable of learning from data to make accurate predictions or decisions. However, the success of any ML model hinges on its ability to perform well not only on the data it was trained on but also on unseen data. This is where performance measures and cross-validation play crucial roles. Together, they provide a robust framework for assessing and validating machine learning models, ensuring their reliability and generalizability.

2 Regression Modeling Basics

Regression modeling and machine learning algorithms are practically important because they enable data-driven decision-making, predictions, and insights across diverse industries. Their importance lies in their ability to process and analyze large volumes of data, uncover patterns, and optimize processes, leading to better outcomes in business, healthcare, technology, and more. Among many regression models, linear regression and logistic regression models are the two most important family models in both classical statistics for association analysis under relatively strong distributional assumptions and modern machine learning fields for prediction with fewer assumptions.

The essence of regression modeling lies in understanding the relationship between a dependent variable and one or more independent variables, or in making predictions based on this relationship.

Assessing the relationship: In this case, regression helps identify how changes in the independent variables (predictors) influence the dependent variable. It is often used to quantify the strength, direction, and nature of the relationship.

Prediction: Regression models can also be used for predicting the value of the dependent variable given new or unseen data for the independent variables. Once the model is trained on historical data, it can be applied to make predictions for future data.

2.1 Modeling Process

The process of regression modeling is an iterative process that includes several key stages, Each step involves feedback loops, enabling the modeler to refine assumptions, data, and model specifications for optimal performance and meaningful insights.

The iterative nature of regression modeling ensures that the model evolves to better fit the data and the problem to improve its performance and alignment with the problem at hand. Here’s a breakdown of the steps

  • Problem Definition: Clearly define the objective of the regression model, including the target variable and predictors, and revisit problem assumptions based on data understanding or business feedback.

  • Data Collection and Preparation Collect, clean, and preprocess data by handling missing values, outliers, and inconsistent entries. Transform features if necessary (e.g., logarithmic transformations for skewed data). Split data into training, validation, and testing sets. Refine data preprocessing steps if initial results indicate poor data quality.

  • Exploratory Data Analysis (EDA): Visualize data to understand relationships and distributions. Identify potential correlations between predictors and the target variable. Check for multicollinearity and other issues. Adjust features or identify new ones based on insights.

  • Model Specification: Choose the type of regression model (e.g., linear regression, polynomial regression) specify the form of the model, and include initial predictors. Test different forms of the model (e.g., adding interaction terms, transforming variables) based on feedback from residual analysis.

  • Model Fitting: Fit the model to the training data using appropriate estimation methods (e.g., Ordinary Least Squares, Maximum Likelihood Estimation). Tune model parameters or adjust features if the initial model does not perform well.

  • Model Evaluation: Evaluate model performance on the training and validation sets using metrics like R-squared, Mean Squared Error (MSE), or Mean Absolute Error (MAE) and check diagnostic plots for residuals to ensure assumptions (linearity, independence, homoscedasticity, and normality) are met. Modify the model if diagnostic checks reveal violations of assumptions.

  • Feature Selection: Use techniques like backward elimination, forward selection, or regularization (Lasso, Ridge) to identify the most important predictors. Re-run the model with selected features and reassess its performance.

  • Model Validation and Testing: Validate the model on a separate validation set to test its ability to generalize to unseen data, perform cross-validation to assess model stability and robustness, and evaluate the model on the test set for final performance metrics. Adjust the model based on validation results to improve generalization.

  • Interpretation and Insight Generation: Interpret the coefficients and their significance to draw actionable insights and ensure the model aligns with domain knowledge and business expectations. Refine the model to improve interpretability or accommodate new insights.

  • Deployment and Monitoring: Deploy the model in production for predictions or decision-making and continuously monitor model performance over time to ensure it remains accurate and relevant. Update or retrain the model as new data becomes available or when performance degrades.


2.2 Linear Regression Models

Linear regression modeling is a foundational technique in statistics and machine learning used to describe the relationship between one or more independent variables (predictors) and a dependent variable (response). Below are the fundamentals of linear regression modeling.

2.2.1 Objectives

Linear regression aims to model the relationship between variables by fitting a linear equation to observed data. The goal is to either predict the dependent variable \(y\) based on the values of independent variables \((x_1, x_2, \cdots, x_k)\) or assess the relationship between \(y\) and predictor variables in the linear and nonlinear forms.

2.2.2 Structure

The general form of the multiple linear regression model is given by \[ y = f(x_1, x_2, \cdots, x_k) + \epsilon \]

where \(f(x_1, x_2, \cdots, x_k)\) is a deterministic expression of predictor variables \(x_1, x_2, \cdots, x_k\) and some unknown parameters (coefficients). \(\epsilon\) is a random variable that follows a certain probability distribution. If the \(f(x)\) is a linear function of unknown parameters and \(\epsilon \sim N(0, \sigma)\), the corresponding model is called linear models. Here are some examples

  • \(f(\cdot)\) is a linear combination of predictor feature variables in the following form

\[ y = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_k x_k + \epsilon, \text{ where } \ \epsilon \sim N(0, \sigma). \]

  • \(f(\cdot)\) is a polynomial function of predictor variables. For example,

\[ y = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \beta_{11}x_1^2 + \beta_{22} x_2^2 + \beta_{12} x_1x_2+ \epsilon, \text{ where } \ \epsilon \sim N(0, \sigma). \] is a polynomial regression model. Caution: The above polynomial (i.e., quadratic) regression is a linear regression model.

2.2.3 Assumptions

Every models and algorithms have some explicit and implicit assumptions to be checked and validated before reporting the model.

    • Data Set: the data must be IID.
    • Structure: the function relationship between the response and the predictor variables must be correctly specified.
    • Distribution of Response Variable: \(Y\sim N[f(x_1, x_2, \cdots, x_k), \sigma]\) which is equivalent to \(\epsilon\sim N(0,\sigma)\). The notation \(N(\mu, \sigma)\) stands for normal distribution with mean \(\mu\) and standard deviation \(\sigma\). These assumptions are crucial to estimate the regression coefficients, define valid p-values for significance tests of regression coefficients, and construct prediction intervals using t-distributions.


2.3 Logistic Regression Model

Logistic regression is a statistical method used to model the relationship between a dependent variable (binary or categorical) and one or more independent variables. It predicts the probability of the dependent variable belonging to a particular category (e.g., success/failure, 1/0) by using a logistic function (sigmoid curve), which maps predicted values to a range between 0 and 1.

2.3.1 Objectives

Logistic regression aims to model the relationship between the binary response variable and other feature variables by fitting a nonlinear equation (of predictor features) to observed data. The goal is to either predict the dependent variable \(y\) based on the values of independent variables \((x_1, x_2, \cdots, x_k)\) or assess the relationship between \(y\) and predictor variables in the linear and nonlinear forms.

2.3.2 Structure

Assume that the response variable takes character values 0 and 1 and \(P(Y = "1")\) is the probability of observing the bigger value of the response (in alphabetical order). The general form of the logistic regression is given by

\[ P(Y=1) = \frac{e^{f(x_1, x_2, \cdots, x_k)}}{1+e^{f(x_1, x_2, \cdots, x_k)}}. \]

The deterministic function \(f(\cdot)\) can take either linear or polynomial expressions of predictor feature variables. For example, if \(f(x_1, x_2, \cdots, x_k) = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_k x_k\), the explicit logistic model is given by

\[ P(Y=1) = \frac{e^{\beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_k x_k}}{1+e^{\beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_k x_k}}. \]

or equivalently

\[ \log \frac{P(Y=1)}{1-P(Y=1)} = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_k x_k. \] The above formulation of the logistic regression model has important terms: The odds of observing \(Y=1\) is \(P(Y=1)/[1-P(Y=1)]\). The left hand of the above formulation is called logarithmic odds of observing \(Y=1\). That is, \[ \text{log odds of (Y = 1)} = \log \frac{P(Y=1)}{1-P(Y=1)}. \]

2.3.3 Assumptions

Different models have different assumptions. Logistic regression models have the following assumptions.

    • Data Set: the data must be IID and the sample size must be large.

    • Structure: the function relationship between \(P(Y=1)\) and the predictor variables must be correctly specified. That is, \(f(\cdot)\) as a function of the predictor variables must be correctly specified.

    • error Distribution: \(Y \sim Binom(p)\) where \(p = P(Y=1)\) is a binomial distribution. These assumptions are crucial to estimate the regression coefficients, define valid p-values for significance tests of regression coefficients, and construct prediction intervals using t-distributions.

2.4 Parameter Interpretation

In predictive analysis and classification applications, the interpretation of the regression coefficient is not the focus but the accuracy of the underlying model and algorithms. However, in association analysis, the interpretation of regression coefficients in the final model is critical for ensuring that models are not only correct but also reliable and responsible in their applications.

The interpretability of regression coefficients is dependent on the structure of the underlying regression model which is also one of the criteria for assessing the goodness of a model. This subsection discusses the interpretation of regression coefficients of linear and logistic regression models.

2.4.1 Linear Regression

We provide an interpretation of the regression coefficient in three different scenarios.

  • First Order Linear Regression

Recall that the first-order linear regression model with \(k\) predictor feature variable has the following form.

\[ y = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_i x_i+ \cdots + \beta_k x_k + \epsilon, \ \ \text{ where } \ \ \epsilon \sim N(0, \sigma). \] where \(1 \le i \le k\) and \(\epsilon \sim N(0, \sigma)\). Let

\[ y^{(i,0)} = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_i (x_i+0)+ \cdots + \beta_k x_k , \]

and

\[ y^{(i,1)} = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_i (x_i+1)+ \cdots + \beta_k x_k, \]

then

\[ \beta_i = y^{(i,1)}- y^{(i,0)}. \]

The coefficient of \(x_i\), represents the change in \(y\) for a one-unit increase in \(x_i\), holding all other variables constant. That is, under the same condition, if \(x_i\) increases one-unit, the corresponding change in \(y\) is the coefficient \(\beta_i\).

  • Polynomial Regressions with no Interaction

Without loss of generality, we consider the following polynomial regression with one predictor variable \(x\).

\[ y = \alpha_0 + \alpha_1 x + \alpha_2 x^2 + \alpha_3 x^3 + \cdots + \alpha_k x^k + \epsilon, \ \ \text{ where } \ \ \epsilon \sim N(0, \sigma). \]

Interpreting polynomial regression coefficients in a practical sense can be challenging. The coefficients in a polynomial regression model correspond to the terms of the polynomial equation and reflect the behavior of the change of the curvature of the (curve of the) polynomial function.

From a practical perspective, we suggest, rather than interpreting coefficients directly, using plots to visualize the relationship between \(x\) and \(y\).

  • Regression with 2nd Order Interaction Only

We will not discuss high-order interaction terms in any polynomial regression model. The following is an end-order polynomial regression model with an interaction term only.

\[ y = \gamma_0 + \gamma_1 x_1 + \gamma_2 x_2 + \gamma_{12}x_1x_2 + \epsilon, \ \ \text{ where } \ \ \epsilon \sim N(0, \sigma) \]

We fix \(x_2\) and increase \(x_1\) by one unit, we evaluate the change of the response \(y\) in the following.

\[ y^{(x_1+1,x_2)} - y^{(x_1,x_2)} = [\gamma_0 + \gamma_1 (x_1+1) + \gamma_2 x_2 + \gamma_{12}(x_1+1)x_2] -[\gamma_0 + \gamma_1 x_1 + \gamma_2 x_2 + \gamma_{12}x_1x_2]= \gamma_1 + \gamma_{12}x_2 \] The coefficient \(\gamma_{12}\) indicates the extent to which the effect of \(x_1\) on \(y\) depends on the value of \(x_2\), and vice versa. A context-specific explanation of the interaction is given in the following agricultural example.

Consider a study examining the effects of fertilizer and water levels on crop yield.

  • \(y\): Crop yield (in tons per hectare).

  • \(x_1\): Amount of fertilizer applied (in kg per hectare).

  • \(x_2\): Amount of water supplied (in liters per hectare).

Assume the regression model is

\[ \text{crop} = \gamma_0 +\gamma_1 \text{water amount}+ \gamma_2\times \text{fertilizer} + \gamma_{12} \text{fertilizer}\times\text{water amount}. \]

The interpretation: when we increase fertilizer by one kg and keep the same amount of water supply, the increment of the crop yield is given by

\[ \text{The increment of crop yield} = \gamma_1 + \gamma_{12} \times \text{water amount (kg)} \] A positive \(\gamma_{12}\) suggests that the benefits of adding fertilizer (\(x_1\)) on crop yield are amplified when water supply \((x_2)\) is high. Similarly, an increased water supply is more beneficial when sufficient fertilizer is applied. That is, doubling water supply while fertilizer levels are already high may lead to more than a proportional increase in yield.

Here’s an example in R to view the interaction effect using a polynomial regression with one two-way interaction term.

# Load necessary library
#library(ggplot2)
# Simulate data
set.seed(123)  # For reproducibility
n = 100
x1 = runif(n, 0, 10)
x2 = runif(n, 0, 10)
z = 3 + 2*x1 - 1.5*x2 + 0.5*x1^2 + 0.3*x1*x2 + rnorm(n, 0, 3)

# Combine into a data frame
data = data.frame(x1 = x1, x2 = x2, y = z)
data$x1x2 = x1*x2

# Fit a quadratic regression model with interaction term
model = lm(z ~ x1 + x2 + I(x1*x2), data = data)
# Summary of the model
#summary(model)
coef1 = coef(model)
data$z1 = coef1[1] + coef1[2]*x1 + coef1[3]*x2 + coef1[4]*data$x1x2 
##
model0 = lm(z~x1+x2, data = data)
coef0 = coef(model0)
data$z0 = coef0[1] + coef0[2]*x1 + coef0[3]*x2  
# Visualizing the quadratic regression surface
quad = plot_ly(data, x= ~x1, y= ~x2, z= ~z1,
               type='mesh3d', 
               intensity = ~z1,
               text = "Interaction",
               colors= colorRamp(rainbow(5))
           )
lin.quad = add_trace(p = quad,
           z = data$z0,
           x = x1,
           y = x2,
           text = "No Interaction",
           type = "mesh3d") %>%
           hide_colorbar() %>%
           layout(title = 'Polynomial Regression Surface: No Interaction vs Interaction',
              margin = list(l = 5, r = 5, b = 50, t = 50, pad = 4))
lin.quad

2.4.2 Logistic Regression

Unlike the linear regression model, the regression coefficients are directly related to the change in the response variable. In the logistic regression, the change of the values of predictor feature variables influences the probability of observing \(Y=1\). If express logistic regression as the logarithm of odds of observing \((Y=1)\), that is,

\[ \log \text{O} =\log \frac{P(Y=1)}{1-P(Y=1)} = \alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \cdots + \alpha_k x_k. \]

\(O = \text{Odds of oberserving Y=1}\). Then we can mimic the way of interpreting the coefficients in linear regression, the i-th regression coefficient in the above multiple logistic regression model is expressed as

\[ \alpha_i = \log \text{O}^{(x_i+1)} - \log \text{O}^{(x_i)} = \log \frac{\text{O}^{(x_i+1)}}{\text{O}^{(x_i)}} \]

We exponentiate both sides of the above equation, and we have

\[ \frac{\text{O}^{(x_i+1)}}{\text{O}^{(x_i)}} = e^{\alpha_i }, \ \ \text{ which is equivalent to }\ \ \frac{\text{O}^{(x_i+1)}-\text{O}^{(x_i)}}{\text{O}^{(x_i)}} = e^{\alpha_i}-1. \] We re-write the last equation as the form of the percentage change in the following.

\[ 100\times \frac{\text{O}^{(x_i+1)}-\text{O}^{(x_i)}}{\text{O}^{(x_i)}} = 100(e^{\alpha_i}-1). \] Therefore, we have the following interpretation of the \(\alpha_i\): under the same condition, increasing \(x_i\) by one unit leads to the change of odds of observing (Y=1) by \(100(e^{\alpha_i}-1)\%\).

To illustrate the relationship between a predictor variable and the response variable with a 2-way interaction, we can use a graphical approach such as interaction plots or 3D surface plots. The following example uses a 3D surface plot to view the relationship in the logistic regression model with a 2nd order interaction in the following example based on simulated data.

# Load necessary library
library(ggplot2)
# Simulate a working data set
set.seed(123)  # For reproducibility
n = 100
x1 = rnorm(n, 0.4,2)
x2 = rexp(n, 1.2)
l0 =.1 + .2*x1 - 1.5*x2  + 0.5*x1*x2
p = exp(l0)/(1+exp(l0))
z = ifelse(p>0.48, 1, 0)
# Combine into a data frame
data = data.frame(x1 = x1, x2 = x2, y = z)
# Fit a quadratic regression model with interaction term
logit0 = glm(y ~ x1 + x2, family = binomial(link=logit), data = data)
# Summary of the model
#summary(logit0)
coef0 = coef(logit0)
ln0 = coef0[1] + coef0[2]*x1 + coef0[3]*x2 
data$z0 = exp(ln0)/(1+exp(ln0))  
###
logit1 = lm(z~x1+x2+x1*x2,family = binomial(link=logit), data = data)
#summary(logit1)
coef1 = coef(logit1)
ln1 = coef1[1] + coef1[2]*x1 + coef1[3]*x2 + coef1[4]*data$x1*x2 
data$z1 = exp(ln1)/(1+exp(ln1))  
# Visualizing the quadratic regression surface
#library(ggplot2)
#library(plot_ly)
quad = plot_ly(data,
               x= ~x1, 
               y= ~x2, 
               z= ~z1,
               type='mesh3d', 
               intensity = ~z1,
               text = "Interaction",
               colors= colorRamp(topo.colors(5, alpha=0.7, rev = TRUE))
           ) 
quad.ln = add_trace(
           p = quad,
           z = ~z0,
           x = ~x1,
           y = ~x2,
           text = "No Interaction",
           type = "mesh3d",
           intensity = ~z0,
           colors= colorRamp(rainbow(5))) %>%
           hide_colorbar() %>%
       layout(title = 'Logistic Surface: No Interaction vs Interaction',
              margin = list(l = 5, r = 5, b = 50, t = 50, pad = 4))
quad.ln

2.5 Paremeter Estimation

The regression coefficients in both (normal) linear and logistic regression models are estimated by maximizing the likelihood function of the parameters - Maximum Likelihood Estimation (MLE).

2.5.1 Concepts of Likeliood (Optional)

Fisher’s Maximum Likelihood Estimation (MLE) is a method for estimating the parameters of a statistical model that maximizes the likelihood function. The likelihood function represents the probability of the observed data given the parameters of the model. Fisher’s MLE is widely used because it has desirable properties under certain conditions, such as consistency, efficiency, and asymptotic normality.

In general, let \(\{x_1, x_2, \cdots, x_n \} \stackrel{\text{i.i.d}}\sim f(x; \alpha, \beta)\), then the likelihood of observing the random sample is defined to be

\[ \mathbb{L}(\alpha, \beta) = \prod_{i=1}^n f(x_i; \alpha, \beta). \] Fisher’s proposal of estimating parameters \((\alpha, \beta)\) is to find the values for \(\alpha\) and \(\beta\), denoted by \((\hat{\alpha}, \hat{\beta})\), that maximize the likelihood \(\mathbb{L}(\alpha, \beta)\). We can use the following notation to denote the above optimization

\[ (\hat{\alpha}, \hat{\beta}) = \arg\max_{(\alpha, \beta)} \mathbb{L}(\alpha, \beta) \]

For computational convenience, Instead of maximizing the likelihood function \(\mathbb{L}(\alpha, \beta)\), we maximize the log-likelihood \(\mathcal{l}(\alpha, \beta) = \log \mathbb{L}(\alpha, \beta)\). This converts the product of probabilities into a sum, simplifying calculations and reducing numerical instability. That is,

\[ (\hat{\alpha}, \hat{\beta}) = \arg\max_{(\alpha, \beta)} \mathbb{l}(\alpha, \beta) \] where

\[ \mathbb{l}(\alpha, \beta) = \sum_{i=1}^n \log f(x_i; \alpha, \beta). \]

2.5.2 Normal Linear Regression Likelihood (Optional)

Without loss of generality, we consider the following normal linear regression model.

\[ Y = \alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \cdots + \alpha_k x_k + \epsilon, \ \ \text{ where } \ \ \epsilon \sim N(0, \sigma) \] Which implies that

\[ \mu = E[Y] = \alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \cdots + \alpha_k x_k \ \ \text{ and } \ \ \text{var}(Y) = \sigma^2. \]

Or equivalent to

\[ Y \sim N(\mu, \sigma). \]

The predictor feature variables \(x_1, x_2, \cdots, x_k\) are assumed to be non-random and the response variable \(y\) is a random variable with the above distribution. The explicit density of \(Y\) is given by

\[ f(y) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{[y-(\alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \cdots + \alpha_k x_k)]^2}{2\sigma^2}}. \]

Let \(\{(y_1, x_{1i}, x_{2i}, \cdots, x_{ki})\}_{i=1}^n \stackrel{\text{i.i.d}}\sim f(y)\), the likelihood of observing the i.i.d sample based on the above distributional assumption is given by

\[ \mathbb{L}(\alpha_0, \alpha_1, \cdots, \alpha_k) = (\sqrt{2\pi}\sigma)^{-n}e^{-\sum_{i=1}^n \frac{[y_i-(\alpha_0 + \alpha_{1} x_{1i} + \alpha_{2} x_{2i} + \cdots + \alpha_k x_{ki})]^2}{2\sigma^2}} \] The log-likelihood function is

\[ \mathbb{l}(\alpha_0, \alpha_1, \cdots, \alpha_k)=-n\log(\sqrt{2\pi}\sigma)-\frac{1}{2\sigma^2}\sum_{i=1}^n [y_i-(\alpha_0 + \alpha_{1} x_{1i} + \alpha_{2} x_{2i} + \cdots + \alpha_k x_{ki})]^2 \]

The MLE is the solution to the above optimization problem.

2.5.3 Logistic Regression Likelihood (Optional)

Note that the response variable in the binary logistic regression model is assumed to be a Bernoulli random variable with success probability \(p\) that is defined as

\[ p = \frac{\exp(\alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \cdots + \alpha_k x_k)}{1 + \exp(\alpha_0 + \alpha_1 x_1 + \alpha_2 x_2 + \cdots + \alpha_k x_k)} \]

with probability mass function

\[ P(Y = y) = p^y(1-p)^{1-y}, \ \ \text{ where } \ \ y = 0 \ \text{ or } \ 1. \]

Let \(\{(y_1, x_{1i}, x_{2i}, \cdots, x_{ki})\}_{i=1}^n \stackrel{\text{i.i.d}}\sim f(y)\), the likelihood of observing the i.i.d sample based on the above distributional assumption is given by

\[ \mathbb{L}(\alpha_0, \alpha_1, \cdots, \alpha_k)=\prod_{i=1}^n p^{y_i}(1-p)^{1-y_i}. \]

The corresponding log-likelihood function is given by

\[ \mathbb{l}(\alpha_0, \alpha_1, \cdots, \alpha_k)=\sum_{i=1}^n[y_i\log p +(1-y_i)\log (1-p)] \]

\[ = \sum_{i=1}^n y_i\left[\frac{\exp(\alpha_0 + \alpha_{1} x_{1i} + \alpha_{2} x_{2i} + \cdots + \alpha_k x_{ki})}{1+\exp(\alpha_0 + \alpha_{1} x_{1i} + \alpha_{2} x_{2i} + \cdots + \alpha_k x_{ki})} \right]\left[1- \frac{\exp(\alpha_0 + \alpha_{1} x_{1i} + \alpha_{2} x_{2i} + \cdots + \alpha_k x_{ki})}{1+\exp(\alpha_0 + \alpha_{1} x_{1i} + \alpha_{2} x_{2i} + \cdots + \alpha_k x_{ki})}\right]. \]

The MLE of the regression coefficients maximizes the above log-likelihood. To be more specific,

\[ (\hat{\alpha}_0, \hat{\alpha}_1, \cdots, \hat{\alpha}_k) = \arg\max_{(\alpha_0, \alpha_1, \cdots, \alpha_k)} \mathbb{L}(\alpha_0, \alpha_1, \cdots, \alpha_k). \]

2.5.4 Finding MLE Using Software Programs

In R, there are several libraries for performing linear and logistic regression, each with unique strengths for different use cases. We will use the popular R built-in data set that was extracted from the 1974 Motor Trend US magazine and comprises fuel consumption and 10 aspects of automobile design and performance for 32 automobiles (1973-74 models).

  • mpg Miles/(US) gallon
  • cyl Number of cylinders
  • disp Displacement (cu.in.)
  • hp Gross horsepower
  • drat Rear axle ratio
  • wt Weight (1000 lbs)
  • qsec 1/4 mile time
  • vs Engine (0 = V-shaped, 1 = straight)
  • am Transmission (0 = automatic, 1 = manual)
  • gear Number of forward gears

1. Base R (stats package)

  • Linear Regression
# Load the data


# Fit a linear regression model
model_lm <- lm(mpg ~ wt + hp, data = mtcars)

# Summary of the model
summary(model_lm)

Call:
lm(formula = mpg ~ wt + hp, data = mtcars)

Residuals:
   Min     1Q Median     3Q    Max 
-3.941 -1.600 -0.182  1.050  5.854 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 37.22727    1.59879  23.285  < 2e-16 ***
wt          -3.87783    0.63273  -6.129 1.12e-06 ***
hp          -0.03177    0.00903  -3.519  0.00145 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.593 on 29 degrees of freedom
Multiple R-squared:  0.8268,    Adjusted R-squared:  0.8148 
F-statistic: 69.21 on 2 and 29 DF,  p-value: 9.109e-12
# Predict new values
predict(model_lm, newdata = data.frame(wt = 3, hp = 100))
       1 
22.41648 
  • Logistic Regression
# Load the data
data(mtcars)
mtcars$am <- as.factor(mtcars$am) # Convert to factor for logistic regression

# Fit a logistic regression model
model_glm <- glm(am ~ wt + hp, family = binomial, data = mtcars)

# Summary of the model
summary(model_glm)

Call:
glm(formula = am ~ wt + hp, family = binomial, data = mtcars)

Coefficients:
            Estimate Std. Error z value Pr(>|z|)   
(Intercept) 18.86630    7.44356   2.535  0.01126 * 
wt          -8.08348    3.06868  -2.634  0.00843 **
hp           0.03626    0.01773   2.044  0.04091 * 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 43.230  on 31  degrees of freedom
Residual deviance: 10.059  on 29  degrees of freedom
AIC: 16.059

Number of Fisher Scoring iterations: 8
# Predict probabilities
predict(model_glm, newdata = data.frame(wt = 3, hp = 100), type = "response")
        1 
0.1469699 

2. MASS Package: Robust Linear Regression

#library(MASS)

# Robust linear regression
model_rlm <- rlm(mpg ~ wt + hp, data = mtcars)

# Summary
summary(model_rlm)

Call: rlm(formula = mpg ~ wt + hp, data = mtcars)
Residuals:
    Min      1Q  Median      3Q     Max 
-3.6639 -1.3057  0.1727  1.3162  6.3392 

Coefficients:
            Value   Std. Error t value
(Intercept) 36.5840  1.4380    25.4407
wt          -3.8801  0.5691    -6.8180
hp          -0.0293  0.0081    -3.6050

Residual standard error: 2.006 on 29 degrees of freedom

3. glmnet Package: Penalized Regression (Lasso and Ridge)

#library(glmnet)

# Prepare data
X <- as.matrix(mtcars[, c("wt", "hp")])
y <- mtcars$mpg

# Fit a ridge regression model (alpha = 0)
model_ridge <- glmnet(X, y, alpha = 0)

# Fit a lasso regression model (alpha = 1)
model_lasso <- glmnet(X, y, alpha = 1)

# Cross-validation for lambda
cv_model <- cv.glmnet(X, y, alpha = 1)

# Best lambda
cv_model$lambda.min
[1] 0.2176783
# Predict using the lasso model
predict(model_lasso, newx = as.matrix(data.frame(wt = 3, hp = 100)), s = cv_model$lambda.min)
           s1
[1,] 22.29602

4. caret Package: Unified Interface for Training Models

#library(caret)

# Linear regression
model_caret_lm <- train(mpg ~ wt + hp, data = mtcars, method = "lm")
summary(model_caret_lm)

Call:
lm(formula = .outcome ~ ., data = dat)

Residuals:
   Min     1Q Median     3Q    Max 
-3.941 -1.600 -0.182  1.050  5.854 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 37.22727    1.59879  23.285  < 2e-16 ***
wt          -3.87783    0.63273  -6.129 1.12e-06 ***
hp          -0.03177    0.00903  -3.519  0.00145 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 2.593 on 29 degrees of freedom
Multiple R-squared:  0.8268,    Adjusted R-squared:  0.8148 
F-statistic: 69.21 on 2 and 29 DF,  p-value: 9.109e-12
# Logistic regression
model_caret_glm <- train(am ~ wt + hp, data = mtcars, method = "glm", family = binomial)
summary(model_caret_glm)

Call:
NULL

Coefficients:
            Estimate Std. Error z value Pr(>|z|)   
(Intercept) 18.86630    7.44356   2.535  0.01126 * 
wt          -8.08348    3.06868  -2.634  0.00843 **
hp           0.03626    0.01773   2.044  0.04091 * 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 43.230  on 31  degrees of freedom
Residual deviance: 10.059  on 29  degrees of freedom
AIC: 16.059

Number of Fisher Scoring iterations: 8

3 Performance Measures

Performance measures are metrics used to assess the quality of statistics machine learning models. They quantify how well a model makes predictions, capturing its strengths and limitations. The choice of performance measures depends on the problem being addressed—classification, regression, clustering, etc.

As previously mentioned, modeling is an iterative process that includes feature engineering, model comparison, validation, and testing based on specific performance measures. We dedicate a standalone section to various metrics.

Next, we summarize different performance metrics based on the analytic tasks associated with various models and algorithms. Some examples from the last subsection of the previous section are used to illustrate the calculation of these performance metrics.

3.1 Regression Performance Metrics

We list the following commonly used performance metrics for regression modeling.

  • Mean Absolute Error (MAE): Represents the average absolute difference between predicted and actual values for regression models with numerical response. The following example shows the calculation of MAE.
# fit a linear regression
ln.model <- lm(mpg ~ wt + hp, data = mtcars)
# fitted/predicted values
fit.val <- fitted(ln.model)
# MAE
MAE = mean(abs(fit.val-mtcars$mpg))
MAE
[1] 1.901484
  • Mean Squared Error (MSE) and Root Mean Squared Error (RMSE): MSE penalizes larger errors more heavily, while RMSE offers interpretability in the same units as the target variable.
# fit a linear regression
ln.model <- lm(mpg ~ wt + hp, data = mtcars)
# fitted/predicted values
fit.val <- fitted(ln.model)
# MSE
MSE = mean((fit.val-mtcars$mpg)^2)
MSE
[1] 6.095242
  • R-squared: Coefficient of determination - indicates the proportion of variance in the target variable explained by the model.
# fit a linear regression
ln.model <- lm(mpg ~ wt + hp, data = mtcars)
#
summary.model = summary(ln.model)
# R.sq
r.sq <- summary.model$r.squared
cbind(R.squared = r.sq)
     R.squared
[1,] 0.8267855
  • AIC and SBC: The Akaike Information Criterion (AIC) and Schwarz Bayesian Criterion (SBC), also known as the Bayesian Information Criterion (BIC), are widely used metrics for model performance evaluation in statistical modeling. They assess the trade-off between model fit and complexity, helping in model selection.
# fit a linear regression
ln.model <- lm(mpg ~ wt + hp, data = mtcars)
# Compute AIC and BIC
aic.value <- AIC(ln.model)
bic.value <- BIC(ln.model)
cbind(AIC=aic.value, SBC = bic.value)
          AIC      SBC
[1,] 156.6523 162.5153

R functions AIC and BIC are generic functions that can be used in glm(). The next example shows how to extract AIC and SBC from the logistic regression model.

# Fit a logistic regression model
model.logit <- glm(am ~ wt + hp, family = binomial, data = mtcars)

# Compute AIC and BIC
aic.logit <- AIC(model.logit)
bic.logit <- BIC(model.logit)
cbind(aic.logit = aic.logit, bic.logit = bic.logit)
     aic.logit bic.logit
[1,]  16.05911  20.45632

3.2 Categorical Regression (Classification) Metrics

We first introduce a few concepts of the confusion matrix and related terms in classification models before introducing the performance metrics.

A confusion matrix is a table used to evaluate the performance of a classification model by comparing its predictions with the actual outcomes. It provides a detailed breakdown of how well the model distinguishes between different classes. The matrix typically consists of four key components:

True Positives (TP): The model correctly predicted the positive class.

True Negatives (TN): The model correctly predicted the negative class.

False Positives (FP): The model incorrectly predicted the positive class (type I error).

False Negatives (FN): The model incorrectly predicted the negative class (type II error).

include_graphics("img/ConfusionMatrix.jpg")
Illustration of the confusion matrix based on a binary classification model.

Illustration of the confusion matrix based on a binary classification model.

  • Accuracy: The ratio of correctly predicted instances to the total number of instances. While widely used, accuracy can be misleading in imbalanced data sets.

  • Precision: Precision measures the proportion of correctly predicted positive instances.

  • Recall: while recall captures the proportion of actual positives identified by the model

  • F1-Score: The F1-score provides a harmonic mean of precision and recall, balancing their trade-off.

\[ \text{F1-Score = 2×(Precision×Recall)/(Precision+Recall) } \]

3.3 Logistic Regression Confusion Matrix

Different confusion matrices for a logistic regression model are entirely dependent on the choice of the threshold probability. The logistic regression model predicts probabilities for the positive class (rather than directly predicting class labels), and these probabilities must be converted into class labels using a threshold. By varying this threshold, we can influence the trade-off between the model’s sensitivity (recall) and specificity. In many software programs and libraries, the default threshold is set to 0.5.

We use the mtcars dataset and fit a logistic regression model using am (types of transmission: manual = 1, automatic = 0), wt (weight), and hp (horsepower) as predictors. The resulting model will predict the probability ( \(P(\text{am} = 1)\) ). Using five threshold predicted probabilities (0.0, 0.25, 0.5, 0.75, 1.0), we produce the corresponding confusion matrices as follows.

# library(caret)
# fit a logistic
model.logit <- glm(am ~ wt + hp, family = binomial, data = mtcars)
# predict probability of P(Y = "Yes")
probabilities <- round(as.vector(predict(model.logit, type = "response")),3)
#
thresholds <- c(0.0, 0.25, 0.5, 0.75, 1.0)

# Loop through thresholds and create confusion matrices
for (threshold in thresholds) {
  cat("\nConfusion Matrix for Threshold =", threshold, "\n")
  
  # Convert probabilities to predictions
  # am: 1 = manual transmission, 0 = automatic transmission
  predictions <- ifelse(probabilities > threshold, "1", "0")
    # Generate confusion matrix
  cm <- confusionMatrix(as.factor(predictions), mtcars$am, positive = "1")
  print(cm$table)
}

Confusion Matrix for Threshold = 0 
          Reference
Prediction  0  1
         0  3  0
         1 16 13

Confusion Matrix for Threshold = 0.25 
          Reference
Prediction  0  1
         0 18  0
         1  1 13

Confusion Matrix for Threshold = 0.5 
          Reference
Prediction  0  1
         0 18  1
         1  1 12

Confusion Matrix for Threshold = 0.75 
          Reference
Prediction  0  1
         0 18  2
         1  1 11

Confusion Matrix for Threshold = 1 
          Reference
Prediction  0  1
         0 19 13
         1  0  0

For convenience, we add 5 additional columns corresponding to the predicted am based on 5 thresholds: 0.0, 0.25, 0.50, 0.75, 1.0.

pred0.00 <- ifelse(probabilities > 0, "1", "0")
pred0.25 <- ifelse(probabilities > 0.25, "1", "0")
pred0.50 <- ifelse(probabilities > 0.50, "1", "0")
pred0.75 <- ifelse(probabilities > 0.75, "1", "0")
pred1.00 <- ifelse(probabilities > 1, "1", "0")
#
Results=data.frame(wt =mtcars$wt, 
                   hp = mtcars$hp, 
                   pred = round(probabilities,3), 
                   am = mtcars$am,
                   am.00 =pred0.00,
                   am.25 =pred0.25,
                   am.50 =pred0.50,
                   am.75 =pred0.75,
                   am1.0 =pred1.00
                   )
## 
Results=Results[order(Results$pred),]
#print(Results)
include_graphics("img/LogitConfusionMatrixExample.jpg")
An illustrative example of creating confusion matrices based on a given set of threshold predicted probabilities

An illustrative example of creating confusion matrices based on a given set of threshold predicted probabilities

3.4 ROC Analysis and AUC

ROC Analysis (Receiver Operating Characteristic Analysis) is a graphical technique used to evaluate the performance of a binary classification model. It plots the True Positive Rate (TPR) (also called sensitivity or recall) against the False Positive Rate (FPR) at various classification thresholds.

  • True Positive Rate (TPR): \(\frac{\text{TP}}{\text{TP} + \text{FN}}\), measures the model’s ability to correctly identify positive cases.

  • False Positive Rate (FPR): \(\frac{\text{FP}}{\text{FP} + \text{TN}}\), measures the proportion of negative cases incorrectly classified as positive.

The ROC curve helps visualize the trade-off between sensitivity and specificity across different thresholds, providing insight into the model’s classification capability.

Using the confusion matrices and the definition of TPR and FPR from the above illustrative example to plot an ROC curve in the following.

# using the above definition of TPR and FPR
# The first number in TPR and FPR representing the top right.
# This is because both TPR and FPR are probabilities
TPR = c(1,13/(13+0), 13/(13+0), 12/(12+1), 11/(2+11), 0/(13+0))
FPR = c(1,3/(16+3), 1/(18+1), 1/(18+1), 1/(1+18), 0/(10+0))
plot(FPR, TPR, type = "b", main = "An Illustrative ROC Curve", col ="blue",
     xlab="1 - Specifity (FPR)", ylab = "Sensitivity (TPR)")
# add a off-diagonal representing random guess algorithm in binary prediction
abline(0,1, lty = 2, col = "red")
# legend
legend("bottomright", c("Logistic Model", "Random Guess"),
       col=c("blue", "red"), lty = 1:2, bty="n", cex = 0.9)
The example of the ROC curve of a logistic regression as a predictive model.

The example of the ROC curve of a logistic regression as a predictive model.

AUC (Area Under the Curve): The AUC quantifies the overall performance of the ROC curve into a single value, ranging from 0 to 1. There is no formula to calculate AUC unless there are specified parametric distributions of \(P(Y=1)\) and \(P(Y=0)\) respectively. The calculation is based on the approximation similar to the Riemann Sum approximation of the area under the curve in Calculus. We illustrate this using the above ROC curve.

# using the above definition of TPR and FPR
# The first number in TPR and FPR represents the top right.
# This is because both TPR and FPR are probabilities
TPR = round(c(1,13/(13+0), 13/(13+0), 12/(12+1), 11/(2+11), 0/(13+0)),3)
FPR = round(c(1,3/(16+3), 1/(18+1), 1/(18+1), 1/(1+18), 0/(10+0)),3)
TPR0 = TPR[7:1]
FPR0 = FPR[7:1]
#plot(FPR0, TPR0, type = "b")
datSenSpe = data.frame(TPR0, FPR0)
ggROC = ggplot(data = datSenSpe, aes(x = FPR0, y=TPR0)) +
        geom_line(col = "steelblue") +
        geom_point(col = "red") +
        geom_segment(x = FPR0, y = 0, xend = FPR0, yend = TPR0, color = 4) +
        geom_segment(x = 0, y = 0, xend = FPR0[7], yend = 0, color = 6) +
        ggtitle("Approximating the AUC of Logistic Model") +
        xlab("1-specificity (FPR)") + 
        ylab("Sensitivity (TPR)") +
        annotate("text", x = 0.025, y = 0.125, label= "A") + 
        annotate("text", x = 0.105, y = 0.5, label = "B") +
        annotate("text", x = 0.605, y = 0.5, label = "C") +
        theme(plot.title = element_text(hjust = 0.5),
              legend.position = c(0.8, 0.2),
              plot.margin = unit(c(0.15, 0.15, 0.75, 0.15), "inches"),
              axis.line = element_line(size = 2, colour = "navy", linetype=1))
# partition the region under the ROC into trapezoids
ggplotly(ggROC)

Illustration of approximating the area of the ROC curve.

In this particular example, the entire region is divided into three sub-regions: A (triangle), B, and C (both rectangles, in general, trapezoids). The three areas are:

  • \(A = (0.053\times 1) /2 = 0.0265\)
  • \(B = [(0.158-0.053)\times 1] = 0.105\)
  • \(C = [(1-0.158)\times 1] = 0.842\)

The AUC (of the ROC curve) is \(AUC = 0.0265 + 0.105 + 0.842 = 0.9735\).

The description of AUC is outlined in the following.
    • An AUC of 1.0 indicates perfect classification (ideal model).

    • An AUC of 0.5 suggests no discriminative ability (random guessing).

    • An AUC below 0.5 implies the model performs worse than random guessing.

In practice, a higher AUC indicates better model performance, as it reflects a higher probability that the classifier ranks a randomly chosen positive instance higher than a randomly chosen negative instance.

Remark: Both ROC (as a visual aid) and AUC are primarily used to compare the performance of multiple models.

ROC With R Libraries

Several R libraries have functions for ROC analysis. One of the frequently used ones is pROC. Next, we use the R function roc() in the R library pROC to find sensitivity and specificity and the AUC we calculated in the above example.

#fig.align='center', fig.width=5, fig.height=5, 
model.logit <- glm(am ~ wt + hp, family = binomial, data = mtcars)
# predict probability of P(Y = "Yes")
probabilities <- round(as.vector(predict(model.logit, type = "response")),3)
##
#  category = ImputedFramingham$TenYearCHD == 1
  ROCobj <- roc(mtcars$am, probabilities)
  Sen <- ROCobj$sensitivities
  Spe <- ROCobj$specificities
  pROCdata <- data.frame(TPR=Sen, FPR = (1 - Spe))
  AUC <- ROCobj$auc
ggpROC = ggplot(data = pROCdata, aes(x = FPR, y=TPR)) +
        geom_line(col = "steelblue") +
        geom_point(col = "red") +
        ggtitle("ROC of Logistic Model Using pROC Library") +
        xlab("1-specificity (FPR)") + 
        ylab("Sensitivity (TPR)") +
        annotate("text", x = 0.605, y = 0.5, label = as,character(AUC)) +
        theme(plot.title = element_text(hjust = 0.5),
              legend.position = c(0.8, 0.2),
              plot.margin = unit(c(0.15, 0.15, 0.75, 0.15), "inches"),
              axis.line = element_line(size = 2, colour = "navy", linetype=1))
# partition the region under the ROC into trapezoids
ggplotly(ggpROC)

Illustration of approximating the area of the ROC curve using pROC library.


3.5 Clustering Metrics

Clustering metrics evaluate the performance of clustering algorithms by measuring the quality of the resulting clusters. These metrics provide insights into how well the clustering algorithm has grouped the data based on its structure or predefined criteria. There are different types of clustering performance measures. We introduce only one measure as an example in the following.

  • Silhouette Score: Measures how similar an instance is to its own cluster compared to other clusters. Denote \(a(i)\) is the mean distance to other points in the same cluster, and \(b(i)\) is the mean distance to points in the nearest cluster. The Silhouette Score is defined as

\[ S(i) = \frac{b(i) - a(i)}{\max \{a(i), b(i)\}} \]

4 K-Fold Cross-Validation

Cross-validation is a computational statistical technique used to estimate the performance of a machine learning model on unseen data. Its essence lies in assessing how well a model generalizes to an independent data set, ensuring that the model isn’t over-fitted to the training data.

Cross-validation in machine learning acts as a method to ensure modeling integrity by preventing a model from being both the “judge” (evaluator) and the “player” (learner) at the same time.

By applying cross-validation, practitioners ensure that models are rigorously tested and refined, leading to better real-world performance and reliable decision-making. As data sets grow and models become more complex, cross-validation will continue to be a critical tool in the pursuit of predictive accuracy and reliability.

4.1 Data Splitting

Data splitting is the process of dividing a data set into separate subsets to train, validate, and test a machine learning model. It ensures that a model is trained on one subset and evaluated on another to generalize well to unseen data. Proper data splitting helps relax statistical assumptions, prevents overfitting, and ensures robust performance evaluation.

Typical Data Splits involve three subsets for different purposes: Training, validation, and testing sets:

include_graphics("img/DataSplitting.jpg")
Typical three-way data splitting for training, validating, and testing.

Typical three-way data splitting for training, validating, and testing.

4.1.1 Training Set

Purpose: The training set is used to train the machine learning model or algorithm just like the regular model fitting process in statistics.

Size: The size of the training data generally consists of 60-80% of the entire data set. One Details: The model learns patterns, weights, and parameters using this subset.

4.1.2 Validation Set (Optional)

Purpose: Used to tune hyperparameters and evaluate model performance during training. Size: Typically 10-20% of the dataset. Details: Helps in model selection and assessing performance without bias from the training data.

4.1.3 Test Set

Purpose: Used to evaluate the final model’s performance on unseen data. Size: Typically 10-20% of the dataset. Details: Provides an unbiased evaluation metric for the model’s generalization ability.

4.2 K-Fold Cross-Validation

In k-fold cross-validation, the data set is divided into k equally sized subsets or folds. The model is trained on k-1 folds and tested on the remaining folds. This process is repeated k times, with each fold serving as the test set once. The final performance metric is averaged over all k iterations, reducing bias and variance in the evaluation.

include_graphics("img/CV-Explanation.jpg")
Graphical representation of the cross-validation algorithm.

Graphical representation of the cross-validation algorithm.

4.2.1 An Analogy

A round-robin sports tournament serves as a direct analogy to cross-validation in machine learning, particularly in its structure and goals.

1. Teams as Data Subsets (Folds)

  • Tournament: Each team in the round-robin tournament represents a participant that competes with every other team.

  • Cross-Validation: The dataset is split into subsets (or folds). Each fold takes turns acting as the “team” being tested while the others serve as “opponents” for training.

2. Matches as Model Training and Validation

  • Tournament: When two teams play a match, one team’s performance is assessed based on its gameplay against the other.

  • Cross-Validation: In each iteration of cross-validation, one fold (team) is treated as the validation set (being evaluated), and the others are combined to form the training set (opponents).

3. Rotational Fairness

  • Tournament: In a round-robin tournament, every team plays against all other teams, ensuring fairness and equal opportunity for assessment.

  • Cross-Validation: Each fold gets the opportunity to serve as the validation set exactly once, ensuring that no data is overlooked during evaluation.

4. Overall Performance as a Measure of Generalization

  • Tournament: A team’s overall ranking is determined by its cumulative performance across all matches, not just a single game.

  • Cross-Validation: The model’s overall performance (e.g., average accuracy or error across folds) is calculated to assess its ability to generalize across the entire dataset, not just on one specific split.

5. Avoiding Overfitting and Bias

  • Tournament: If one team wins by focusing on a single opponent’s weakness but loses to others, its strategy isn’t robust. Round-robin ensures that each team’s overall strategy is evaluated.

  • Cross-Validation: By rotating validation sets, cross-validation ensures that the model is not overly tuned to specific subsets of the data, reducing the risk of overfitting and providing a fair evaluation.

4.2.2 An Example

1. Working Data Set and Practical Question

Data from n = 113 hospitals in the United States are used to predict the risk of a hospital patient acquiring an infection during their stay, based on factors identified by domain experts. The data set includes 11 variables. Note that this is not a formal data analysis; we will not perform exploratory data analysis (EDA) or feature engineering. Below are a few variables used in this illustrative example.

InfctRsk: infection risk (\(y\))

Stay: average length of patient stay (\(x_1\))

Age: average patient age (\(x_2\))

Xray: the measure of how many X-rays are given in the hospital (\(x_3\))


2. Models to Address The Question

Since the primary use of cross-validation is to compare performance among different models and assist optimal model selection, we will build the following two linear models and use the 5-fold cross-validation method to identify the optimal model.

\[ \text{Model 1: } \ \ \text{InfctRsk} = \alpha_0 + \alpha_1\times \text{Stay} + \alpha_2 \times \text{Age} + \alpha_3 \times \text{Xray} \]

\[ \text{Model 2: } \ \ \text{InfctRsk} = \alpha_0 + \alpha_1\times \text{Stay} + \alpha_2 \times \text{Xray} \]

3. Model Selection via 5-fold Cross-Validation

We use 5-fold cross-validation to select the better one from Model 1 and Model 2 without using any inferential statistics such as testing procedures or likelihood-based metrics such as AIC or SBC (BIC).

  • Data Splitting: Using random splitting to partition the data into training set (100) and testing set (31).

  • 5-fold cross-validation: splitting the training set into 5 folds with equal size to perform cross-validation.

  • Choose Performance Measure: The resulting model will be used to predict the infection risk. We use the mean square error to measure the predictive performance.

  • Computing and Coding: We will code the CV algorithm directly without using any built-in function with CV capability to enhance technical understanding of the concept. The code can be generalized to other models.

infection =read.table("https://pengdsci.github.io/STA552/datsets/hospital_infct.txt", header = TRUE)
# using sample() to perform random splitting
train.ID = sample(1:dim(infection)[1], 100, replace = FALSE)  # without replacement
# training set
train = infection[train.ID,]
test = infection[-train.ID,]
## splitting the train set into 5 folds to train and validate the candidate models
N = dim(train)[1]   # size of training data
k = 5               # number of folds
fld.n = ceiling(N/k)
MSE.m1 = NULL       # null vector to store MSE
MSE.m2 = NULL      
for (i in 1:k){
  valid.ID = ((i-1)*fld.n +1):(i*fld.n)  # observation ID for the i-th validation set 
  valid.set = train[valid.ID, ]
  train.set = train[-valid.ID,]
  ## fitting two candidate models with combined 4 folds of data set
  M01 = lm(InfctRsk ~  Stay + Age + Xray, data = train.set)
  M02 = lm(InfctRsk ~  Stay + Xray, data = train.set)
  ## Predicting InfectRsk using the two candidate models based on the validate set
  predM01 = predict(M01, newdata = valid.set)
  predM02 = predict(M02, newdata = valid.set)
  ## calculating the MSE associated with the two models
  MSE.m1[i] = mean((predM01 - valid.set$InfctRsk)^2)
  MSE.m2[i] = mean((predM02 - valid.set$InfctRsk)^2)
}
## define a data frame to store the MSE of the candidate models
## 
MSE = data.frame(fold = rep(1:k,2), MSE = c(MSE.m1, MSE.m2), type=c(rep("Model 1",k), rep("Model 2", k)))
## line plots of the 
cvplot = ggplot(data = MSE, aes(x=fold, y=MSE, color = type)) +
         geom_line() +
         geom_point() +
         coord_cartesian(xlim = c(0, 6),
                         ylim = c(0,2)) +
         geom_text(mapping = aes(x=1.0, y=0.25, 
                  label=paste("Model 1 Mean MSE: = ", round(mean(MSE.m1),3), "")), 
                   hjust=0) +
         geom_text(mapping = aes(x=1.0, y=0.15, 
                  label=paste("Model 2 Mean MSE: = ", round(mean(MSE.m2),3), "")), 
                   hjust=0) + 
         ggtitle("Line plots of MSE candidate Models across folds") +
         theme(plot.title = element_text(hjust = 0.5),
               plot.margin = unit(c(1,1,1,1), "cm"))
ggplotly(cvplot)

The line plot of folds MSEs for candidate models.

  • Reporting the performance of the final optimal model: predict find MSE based on the hold-up test set. This MSE is supposed to be similar to the actual MSE when implementing the final model to unseen future data in two steps.
    • Fit the final model to the entire train set.
    • Use the fitted final model (i.e., regression coefficients are based on the entire train set) to predict InfectRsk
    • find the MSE using the predicted InfectRsk and corresponding true InfecRsk to report.
if (mean(MSE.m1) < mean(MSE.m2)){
  model.test = lm(InfctRsk ~  Stay + Age + Xray, data = train)
  pred.model.test = predict(model.test, newdata = test)
  test.MSE = round(mean((pred.model.test-test$InfctRsk)^2),3)
  final.test.plot = cvplot +
       geom_text(mapping = aes(x=5.0, y=0.2,
                  label=paste("Final Model Test MSE: = ", round(mean(test.MSE),3), "")), 
                   hjust=0, ) 
  ggplotly(final.test.plot)
} else{
  model.test = lm(InfctRsk ~  Stay + Xray, data = train)
  pred.model.test = predict(model.test, newdata = test)
  test.MSE = round(mean((pred.model.test-test$InfctRsk)^2),3)
  final.test.plot = cvplot +
       geom_text(mapping = aes(x=5.0, y=0.2,
                  label=paste("Final Model Test MSE: = ", round(mean(test.MSE),3), "")), 
                   hjust=0)
  ggplotly(final.test.plot)
}

The line plot of folds MSEs for candidate models as well as the testing MSE of the final model.

  • Handing the final model for implementation/deployment: The regression coefficients of the final working model for implementation/deployment should be updated based on the entire data set (i.e., combine the training and testing sets).
LS0tDQp0aXRsZTogJ1JlZ3Jlc3Npb24gUGVyZm9ybWFuY2UgTWVhc3VyZXMgYW5kIENyb3NzLXZhbGlkYXRpb24nDQphdXRob3I6ICJDaGVuZyBQZW5nIg0KZGF0ZTogIldlc3QgQ2hlc3RlciBVbml2ZXJzaXR5ICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IHNwYWNlbGFiDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IG5hdnk7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMTRweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxNnB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE0cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikNCmxpYnJhcnkoR0dhbGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpsaWJyYXJ5KGNhcmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCmxpYnJhcnkocGxvdGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJnbG1uZXQiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikNCmxpYnJhcnkoZ2xtbmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJNQVNTIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIk1BU1MiKQ0KbGlicmFyeShNQVNTKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpsaWJyYXJ5KGNhcmV0KQ0KfQ0KaWYgKCFyZXF1aXJlKCJwUk9DIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKQ0KbGlicmFyeShwUk9DKQ0KfQ0KIyAgDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgIyBzdXBwcmVzcyB3YXJuaW5nIG1lc3NhZ2VzLA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgIyBpbmNsdWRlIHRoZSBvdXRwdXQgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkENCiAgICAgICAgICAgICAgICAgICAgICApICANCmBgYA0KXA0KDQojIEludHJvZHVjdGlvbg0KDQpNYWNoaW5lIGxlYXJuaW5nIChNTCkgaGFzIHJldm9sdXRpb25pemVkIHZhcmlvdXMgaW5kdXN0cmllcyBieSBlbmFibGluZyB0aGUgZGV2ZWxvcG1lbnQgb2Ygc3lzdGVtcyBjYXBhYmxlIG9mIGxlYXJuaW5nIGZyb20gZGF0YSB0byBtYWtlIGFjY3VyYXRlIHByZWRpY3Rpb25zIG9yIGRlY2lzaW9ucy4gSG93ZXZlciwgdGhlIHN1Y2Nlc3Mgb2YgYW55IE1MIG1vZGVsIGhpbmdlcyBvbiBpdHMgYWJpbGl0eSB0byBwZXJmb3JtIHdlbGwgbm90IG9ubHkgb24gdGhlIGRhdGEgaXQgd2FzIHRyYWluZWQgb24gYnV0IGFsc28gb24gdW5zZWVuIGRhdGEuIFRoaXMgaXMgd2hlcmUgcGVyZm9ybWFuY2UgbWVhc3VyZXMgYW5kIGNyb3NzLXZhbGlkYXRpb24gcGxheSBjcnVjaWFsIHJvbGVzLiBUb2dldGhlciwgdGhleSBwcm92aWRlIGEgcm9idXN0IGZyYW1ld29yayBmb3IgYXNzZXNzaW5nIGFuZCB2YWxpZGF0aW5nIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLCBlbnN1cmluZyB0aGVpciByZWxpYWJpbGl0eSBhbmQgZ2VuZXJhbGl6YWJpbGl0eS4NCg0KDQoNCiMgUmVncmVzc2lvbiBNb2RlbGluZyBCYXNpY3MNCg0KUmVncmVzc2lvbiBtb2RlbGluZyBhbmQgbWFjaGluZSBsZWFybmluZyBhbGdvcml0aG1zIGFyZSBwcmFjdGljYWxseSBpbXBvcnRhbnQgYmVjYXVzZSB0aGV5IGVuYWJsZSBkYXRhLWRyaXZlbiBkZWNpc2lvbi1tYWtpbmcsIHByZWRpY3Rpb25zLCBhbmQgaW5zaWdodHMgYWNyb3NzIGRpdmVyc2UgaW5kdXN0cmllcy4gVGhlaXIgaW1wb3J0YW5jZSBsaWVzIGluIHRoZWlyIGFiaWxpdHkgdG8gcHJvY2VzcyBhbmQgYW5hbHl6ZSBsYXJnZSB2b2x1bWVzIG9mIGRhdGEsIHVuY292ZXIgcGF0dGVybnMsIGFuZCBvcHRpbWl6ZSBwcm9jZXNzZXMsIGxlYWRpbmcgdG8gYmV0dGVyIG91dGNvbWVzIGluIGJ1c2luZXNzLCBoZWFsdGhjYXJlLCB0ZWNobm9sb2d5LCBhbmQgbW9yZS4gQW1vbmcgbWFueSByZWdyZXNzaW9uIG1vZGVscywgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIGFyZSB0aGUgdHdvIG1vc3QgaW1wb3J0YW50IGZhbWlseSBtb2RlbHMgaW4gYm90aCBjbGFzc2ljYWwgc3RhdGlzdGljcyBmb3IgYXNzb2NpYXRpb24gYW5hbHlzaXMgdW5kZXIgcmVsYXRpdmVseSBzdHJvbmcgZGlzdHJpYnV0aW9uYWwgYXNzdW1wdGlvbnMgYW5kIG1vZGVybiBtYWNoaW5lIGxlYXJuaW5nIGZpZWxkcyBmb3IgcHJlZGljdGlvbiB3aXRoIGZld2VyIGFzc3VtcHRpb25zLiANCg0KVGhlIGVzc2VuY2Ugb2YgcmVncmVzc2lvbiBtb2RlbGluZyBsaWVzIGluIHVuZGVyc3RhbmRpbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgZGVwZW5kZW50IHZhcmlhYmxlIGFuZCBvbmUgb3IgbW9yZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMsIG9yIGluIG1ha2luZyBwcmVkaWN0aW9ucyBiYXNlZCBvbiB0aGlzIHJlbGF0aW9uc2hpcC4NCg0KKipBc3Nlc3NpbmcgdGhlIHJlbGF0aW9uc2hpcCoqOiBJbiB0aGlzIGNhc2UsIHJlZ3Jlc3Npb24gaGVscHMgaWRlbnRpZnkgaG93IGNoYW5nZXMgaW4gdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyAocHJlZGljdG9ycykgaW5mbHVlbmNlIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUuIEl0IGlzIG9mdGVuIHVzZWQgdG8gcXVhbnRpZnkgdGhlIHN0cmVuZ3RoLCBkaXJlY3Rpb24sIGFuZCBuYXR1cmUgb2YgdGhlIHJlbGF0aW9uc2hpcC4NCg0KKipQcmVkaWN0aW9uKio6IFJlZ3Jlc3Npb24gbW9kZWxzIGNhbiBhbHNvIGJlIHVzZWQgZm9yIHByZWRpY3RpbmcgdGhlIHZhbHVlIG9mIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgZ2l2ZW4gbmV3IG9yIHVuc2VlbiBkYXRhIGZvciB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiBPbmNlIHRoZSBtb2RlbCBpcyB0cmFpbmVkIG9uIGhpc3RvcmljYWwgZGF0YSwgaXQgY2FuIGJlIGFwcGxpZWQgdG8gbWFrZSBwcmVkaWN0aW9ucyBmb3IgZnV0dXJlIGRhdGEuDQoNCiMjIE1vZGVsaW5nIFByb2Nlc3MNCg0KVGhlIHByb2Nlc3Mgb2YgcmVncmVzc2lvbiBtb2RlbGluZyBpcyBhbiBpdGVyYXRpdmUgcHJvY2VzcyB0aGF0IGluY2x1ZGVzIHNldmVyYWwga2V5IHN0YWdlcywgRWFjaCBzdGVwIGludm9sdmVzIGZlZWRiYWNrIGxvb3BzLCBlbmFibGluZyB0aGUgbW9kZWxlciB0byByZWZpbmUgYXNzdW1wdGlvbnMsIGRhdGEsIGFuZCBtb2RlbCBzcGVjaWZpY2F0aW9ucyBmb3Igb3B0aW1hbCBwZXJmb3JtYW5jZSBhbmQgbWVhbmluZ2Z1bCBpbnNpZ2h0cy4NCg0KVGhlIGl0ZXJhdGl2ZSBuYXR1cmUgb2YgcmVncmVzc2lvbiBtb2RlbGluZyBlbnN1cmVzIHRoYXQgdGhlIG1vZGVsIGV2b2x2ZXMgdG8gYmV0dGVyIGZpdCB0aGUgZGF0YSBhbmQgdGhlIHByb2JsZW0gdG8gaW1wcm92ZSBpdHMgcGVyZm9ybWFuY2UgYW5kIGFsaWdubWVudCB3aXRoIHRoZSBwcm9ibGVtIGF0IGhhbmQuIEhlcmUncyBhIGJyZWFrZG93biBvZiB0aGUgc3RlcHMgDQoNCiogKipQcm9ibGVtIERlZmluaXRpb24qKjogQ2xlYXJseSBkZWZpbmUgdGhlIG9iamVjdGl2ZSBvZiB0aGUgcmVncmVzc2lvbiBtb2RlbCwgaW5jbHVkaW5nIHRoZSB0YXJnZXQgdmFyaWFibGUgYW5kIHByZWRpY3RvcnMsIGFuZCAqKnJldmlzaXQqKiBwcm9ibGVtIGFzc3VtcHRpb25zIGJhc2VkIG9uIGRhdGEgdW5kZXJzdGFuZGluZyBvciBidXNpbmVzcyBmZWVkYmFjay4NCg0KKiAqKkRhdGEgQ29sbGVjdGlvbiBhbmQgUHJlcGFyYXRpb24qKiBDb2xsZWN0LCBjbGVhbiwgYW5kIHByZXByb2Nlc3MgZGF0YSBieSBoYW5kbGluZyBtaXNzaW5nIHZhbHVlcywgb3V0bGllcnMsIGFuZCBpbmNvbnNpc3RlbnQgZW50cmllcy4gVHJhbnNmb3JtIGZlYXR1cmVzIGlmIG5lY2Vzc2FyeSAoZS5nLiwgbG9nYXJpdGhtaWMgdHJhbnNmb3JtYXRpb25zIGZvciBza2V3ZWQgZGF0YSkuDQpTcGxpdCBkYXRhIGludG8gdHJhaW5pbmcsIHZhbGlkYXRpb24sIGFuZCB0ZXN0aW5nIHNldHMuICoqUmVmaW5lKiogZGF0YSBwcmVwcm9jZXNzaW5nIHN0ZXBzIGlmIGluaXRpYWwgcmVzdWx0cyBpbmRpY2F0ZSBwb29yIGRhdGEgcXVhbGl0eS4NCg0KKiAgKipFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpKio6IFZpc3VhbGl6ZSBkYXRhIHRvIHVuZGVyc3RhbmQgcmVsYXRpb25zaGlwcyBhbmQgZGlzdHJpYnV0aW9ucy4NCklkZW50aWZ5IHBvdGVudGlhbCBjb3JyZWxhdGlvbnMgYmV0d2VlbiBwcmVkaWN0b3JzIGFuZCB0aGUgdGFyZ2V0IHZhcmlhYmxlLiBDaGVjayBmb3IgbXVsdGljb2xsaW5lYXJpdHkgYW5kIG90aGVyIGlzc3Vlcy4gKipBZGp1c3QqKiBmZWF0dXJlcyBvciBpZGVudGlmeSBuZXcgb25lcyBiYXNlZCBvbiBpbnNpZ2h0cy4NCg0KKiAqKk1vZGVsIFNwZWNpZmljYXRpb24qKjogQ2hvb3NlIHRoZSB0eXBlIG9mIHJlZ3Jlc3Npb24gbW9kZWwgKGUuZy4sIGxpbmVhciByZWdyZXNzaW9uLCBwb2x5bm9taWFsIHJlZ3Jlc3Npb24pIHNwZWNpZnkgdGhlIGZvcm0gb2YgdGhlIG1vZGVsLCBhbmQgaW5jbHVkZSBpbml0aWFsIHByZWRpY3RvcnMuICAqKlRlc3QgZGlmZmVyZW50IGZvcm1zKiogb2YgdGhlIG1vZGVsIChlLmcuLCBhZGRpbmcgaW50ZXJhY3Rpb24gdGVybXMsIHRyYW5zZm9ybWluZyB2YXJpYWJsZXMpIGJhc2VkIG9uIGZlZWRiYWNrIGZyb20gcmVzaWR1YWwgYW5hbHlzaXMuDQoNCiogKipNb2RlbCBGaXR0aW5nKio6IEZpdCB0aGUgbW9kZWwgdG8gdGhlIHRyYWluaW5nIGRhdGEgdXNpbmcgYXBwcm9wcmlhdGUgZXN0aW1hdGlvbiBtZXRob2RzIChlLmcuLCBPcmRpbmFyeSBMZWFzdCBTcXVhcmVzLCBNYXhpbXVtIExpa2VsaWhvb2QgRXN0aW1hdGlvbikuICoqVHVuZSBtb2RlbCBwYXJhbWV0ZXJzIG9yIGFkanVzdCBmZWF0dXJlcyoqIGlmIHRoZSBpbml0aWFsIG1vZGVsIGRvZXMgbm90IHBlcmZvcm0gd2VsbC4NCg0KKiAqKk1vZGVsIEV2YWx1YXRpb24qKjogRXZhbHVhdGUgbW9kZWwgcGVyZm9ybWFuY2Ugb24gdGhlIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIHNldHMgdXNpbmcgbWV0cmljcyBsaWtlIFItc3F1YXJlZCwgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpLCBvciBNZWFuIEFic29sdXRlIEVycm9yIChNQUUpIGFuZCBjaGVjayBkaWFnbm9zdGljIHBsb3RzIGZvciByZXNpZHVhbHMgdG8gZW5zdXJlIGFzc3VtcHRpb25zIChsaW5lYXJpdHksIGluZGVwZW5kZW5jZSwgaG9tb3NjZWRhc3RpY2l0eSwgYW5kIG5vcm1hbGl0eSkgYXJlIG1ldC4gKipNb2RpZnkqKiB0aGUgbW9kZWwgaWYgZGlhZ25vc3RpYyBjaGVja3MgcmV2ZWFsIHZpb2xhdGlvbnMgb2YgYXNzdW1wdGlvbnMuDQoNCiogKipGZWF0dXJlIFNlbGVjdGlvbioqOiBVc2UgdGVjaG5pcXVlcyBsaWtlIGJhY2t3YXJkIGVsaW1pbmF0aW9uLCBmb3J3YXJkIHNlbGVjdGlvbiwgb3IgcmVndWxhcml6YXRpb24gKExhc3NvLCBSaWRnZSkgdG8gaWRlbnRpZnkgdGhlIG1vc3QgaW1wb3J0YW50IHByZWRpY3RvcnMuICoqUmUtcnVuKiogdGhlIG1vZGVsIHdpdGggc2VsZWN0ZWQgZmVhdHVyZXMgYW5kIHJlYXNzZXNzIGl0cyBwZXJmb3JtYW5jZS4NCg0KKiAqKk1vZGVsIFZhbGlkYXRpb24gYW5kIFRlc3RpbmcqKjogVmFsaWRhdGUgdGhlIG1vZGVsIG9uIGEgc2VwYXJhdGUgdmFsaWRhdGlvbiBzZXQgdG8gdGVzdCBpdHMgYWJpbGl0eSB0byBnZW5lcmFsaXplIHRvIHVuc2VlbiBkYXRhLCBwZXJmb3JtIGNyb3NzLXZhbGlkYXRpb24gdG8gYXNzZXNzIG1vZGVsIHN0YWJpbGl0eSBhbmQgcm9idXN0bmVzcywgYW5kIGV2YWx1YXRlIHRoZSBtb2RlbCBvbiB0aGUgdGVzdCBzZXQgZm9yIGZpbmFsIHBlcmZvcm1hbmNlIG1ldHJpY3MuICoqQWRqdXN0KiogdGhlIG1vZGVsIGJhc2VkIG9uIHZhbGlkYXRpb24gcmVzdWx0cyB0byBpbXByb3ZlIGdlbmVyYWxpemF0aW9uLg0KDQoqICoqSW50ZXJwcmV0YXRpb24gYW5kIEluc2lnaHQgR2VuZXJhdGlvbioqOiBJbnRlcnByZXQgdGhlIGNvZWZmaWNpZW50cyBhbmQgdGhlaXIgc2lnbmlmaWNhbmNlIHRvIGRyYXcgYWN0aW9uYWJsZSBpbnNpZ2h0cyBhbmQgZW5zdXJlIHRoZSBtb2RlbCBhbGlnbnMgd2l0aCBkb21haW4ga25vd2xlZGdlIGFuZCBidXNpbmVzcyBleHBlY3RhdGlvbnMuICoqUmVmaW5lKiogdGhlIG1vZGVsIHRvIGltcHJvdmUgaW50ZXJwcmV0YWJpbGl0eSBvciBhY2NvbW1vZGF0ZSBuZXcgaW5zaWdodHMuDQoNCiogKipEZXBsb3ltZW50IGFuZCBNb25pdG9yaW5nKio6IERlcGxveSB0aGUgbW9kZWwgaW4gcHJvZHVjdGlvbiBmb3IgcHJlZGljdGlvbnMgb3IgZGVjaXNpb24tbWFraW5nIGFuZCBjb250aW51b3VzbHkgbW9uaXRvciBtb2RlbCBwZXJmb3JtYW5jZSBvdmVyIHRpbWUgdG8gZW5zdXJlIGl0IHJlbWFpbnMgYWNjdXJhdGUgYW5kIHJlbGV2YW50LiAqKlVwZGF0ZSBvciByZXRyYWluKiogdGhlIG1vZGVsIGFzIG5ldyBkYXRhIGJlY29tZXMgYXZhaWxhYmxlIG9yIHdoZW4gcGVyZm9ybWFuY2UgZGVncmFkZXMuDQoNClwNCg0KIyMgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWxzDQoNCkxpbmVhciByZWdyZXNzaW9uIG1vZGVsaW5nIGlzIGEgZm91bmRhdGlvbmFsIHRlY2huaXF1ZSBpbiBzdGF0aXN0aWNzIGFuZCBtYWNoaW5lIGxlYXJuaW5nIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG9uZSBvciBtb3JlIGluZGVwZW5kZW50IHZhcmlhYmxlcyAocHJlZGljdG9ycykgYW5kIGEgZGVwZW5kZW50IHZhcmlhYmxlIChyZXNwb25zZSkuIEJlbG93IGFyZSB0aGUgZnVuZGFtZW50YWxzIG9mIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsaW5nLg0KDQoNCiMjIyAqKk9iamVjdGl2ZXMqKg0KDQpMaW5lYXIgcmVncmVzc2lvbiBhaW1zIHRvIG1vZGVsIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXMgYnkgZml0dGluZyBhIGxpbmVhciBlcXVhdGlvbiB0byBvYnNlcnZlZCBkYXRhLiBUaGUgZ29hbCBpcyB0byBlaXRoZXIgcHJlZGljdCB0aGUgZGVwZW5kZW50IHZhcmlhYmxlICR5JCBiYXNlZCBvbiB0aGUgdmFsdWVzIG9mIGluZGVwZW5kZW50IHZhcmlhYmxlcyAkKHhfMSwgeF8yLCBcY2RvdHMsIHhfaykkIG9yIGFzc2VzcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gJHkkIGFuZCBwcmVkaWN0b3IgdmFyaWFibGVzIGluIHRoZSBsaW5lYXIgYW5kIG5vbmxpbmVhciBmb3Jtcy4NCiAgDQojIyMgKipTdHJ1Y3R1cmUqKg0KDQpUaGUgZ2VuZXJhbCBmb3JtIG9mIHRoZSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpcyBnaXZlbiBieQ0KJCQNCnkgPSBmKHhfMSwgeF8yLCBcY2RvdHMsIHhfaykgKyBcZXBzaWxvbg0KJCQNCg0Kd2hlcmUgJGYoeF8xLCB4XzIsIFxjZG90cywgeF9rKSQgaXMgYSBkZXRlcm1pbmlzdGljIGV4cHJlc3Npb24gb2YgcHJlZGljdG9yIHZhcmlhYmxlcyAkeF8xLCB4XzIsIFxjZG90cywgeF9rJCBhbmQgc29tZSB1bmtub3duIHBhcmFtZXRlcnMgKGNvZWZmaWNpZW50cykuICRcZXBzaWxvbiQgaXMgYSByYW5kb20gdmFyaWFibGUgdGhhdCBmb2xsb3dzIGEgY2VydGFpbiBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24uIElmIHRoZSAkZih4KSQgaXMgYSAqKmxpbmVhciBmdW5jdGlvbiBvZiB1bmtub3duIHBhcmFtZXRlcnMqKiBhbmQgJFxlcHNpbG9uIFxzaW0gTigwLCBcc2lnbWEpJCwgdGhlIGNvcnJlc3BvbmRpbmcgbW9kZWwgaXMgY2FsbGVkICoqbGluZWFyIG1vZGVscyoqLiBIZXJlIGFyZSBzb21lIGV4YW1wbGVzDQoNCiogJGYoXGNkb3QpJCBpcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiBwcmVkaWN0b3IgZmVhdHVyZSB2YXJpYWJsZXMgaW4gdGhlIGZvbGxvd2luZyBmb3JtICAgDQoNCiQkDQp5ID0gXGJldGFfMCArIFxiZXRhXzEgeF8xICsgXGJldGFfMiB4XzIgKyBcY2RvdHMgKyBcYmV0YV9rIHhfayArIFxlcHNpbG9uLCBcdGV4dHsgd2hlcmUgfSBcIFxlcHNpbG9uIFxzaW0gTigwLCBcc2lnbWEpLg0KJCQNCg0KKiAkZihcY2RvdCkkIGlzIGEgcG9seW5vbWlhbCBmdW5jdGlvbiBvZiBwcmVkaWN0b3IgdmFyaWFibGVzLiBGb3IgZXhhbXBsZSwNCg0KJCQNCnkgPSBcYmV0YV8wICsgXGJldGFfMSB4XzEgKyBcYmV0YV8yIHhfMiArIFxiZXRhX3sxMX14XzFeMiArIFxiZXRhX3syMn0geF8yXjIgKyBcYmV0YV97MTJ9IHhfMXhfMisgXGVwc2lsb24sIFx0ZXh0eyB3aGVyZSB9IFwgXGVwc2lsb24gXHNpbSBOKDAsIFxzaWdtYSkuDQokJA0KaXMgYSBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwuIDxmb250IGNvbG9yID0gInJlZCI+KipcY29sb3J7cmVkfUNhdXRpb24qKjogKlRoZSBhYm92ZSBwb2x5bm9taWFsIChpLmUuLCBxdWFkcmF0aWMpIHJlZ3Jlc3Npb24gaXMgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbC4qPC9mb250Pg0KDQoNCiMjIyAqKkFzc3VtcHRpb25zKioNCg0KRXZlcnkgbW9kZWxzIGFuZCBhbGdvcml0aG1zIGhhdmUgc29tZSBleHBsaWNpdCBhbmQgaW1wbGljaXQgYXNzdW1wdGlvbnMgdG8gYmUgY2hlY2tlZCBhbmQgdmFsaWRhdGVkIGJlZm9yZSByZXBvcnRpbmcgdGhlIG1vZGVsLg0KDQo8dWw+DQoqICoqRGF0YSBTZXQqKjogdGhlIGRhdGEgbXVzdCBiZSBJSUQuDQoqICoqU3RydWN0dXJlKio6IHRoZSBmdW5jdGlvbiByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcmVzcG9uc2UgYW5kIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIG11c3QgYmUgY29ycmVjdGx5IHNwZWNpZmllZC4NCiogKipEaXN0cmlidXRpb24gb2YgUmVzcG9uc2UgVmFyaWFibGUqKjogJFlcc2ltIE5bZih4XzEsIHhfMiwgXGNkb3RzLCB4X2spLCBcc2lnbWFdJCB3aGljaCBpcyBlcXVpdmFsZW50IHRvICRcZXBzaWxvblxzaW0gTigwLFxzaWdtYSkkLiBUaGUgbm90YXRpb24gJE4oXG11LCBcc2lnbWEpJCBzdGFuZHMgZm9yIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuICRcbXUkIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gJFxzaWdtYSQuIDxmb250IGNvbG9yID0gInJlZCI+KipcY29sb3J7cmVkfVRoZXNlIGFzc3VtcHRpb25zIGFyZSBjcnVjaWFsIHRvIGVzdGltYXRlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgZGVmaW5lIHZhbGlkIHAtdmFsdWVzIGZvciBzaWduaWZpY2FuY2UgdGVzdHMgb2YgcmVncmVzc2lvbiBjb2VmZmljaWVudHMsIGFuZCBjb25zdHJ1Y3QgcHJlZGljdGlvbiBpbnRlcnZhbHMgdXNpbmcgdC1kaXN0cmlidXRpb25zLioqPC9mb250Pg0KDQo8L3VsPg0KDQpcDQoNCg0KIyMgTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbA0KDQpMb2dpc3RpYyByZWdyZXNzaW9uIGlzIGEgc3RhdGlzdGljYWwgbWV0aG9kIHVzZWQgdG8gbW9kZWwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgZGVwZW5kZW50IHZhcmlhYmxlIChiaW5hcnkgb3IgY2F0ZWdvcmljYWwpIGFuZCBvbmUgb3IgbW9yZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIEl0IHByZWRpY3RzIHRoZSBwcm9iYWJpbGl0eSBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGJlbG9uZ2luZyB0byBhIHBhcnRpY3VsYXIgY2F0ZWdvcnkgKGUuZy4sIHN1Y2Nlc3MvZmFpbHVyZSwgMS8wKSBieSB1c2luZyBhIGxvZ2lzdGljIGZ1bmN0aW9uIChzaWdtb2lkIGN1cnZlKSwgd2hpY2ggbWFwcyBwcmVkaWN0ZWQgdmFsdWVzIHRvIGEgcmFuZ2UgYmV0d2VlbiAwIGFuZCAxLg0KDQoNCiMjIyAqKk9iamVjdGl2ZXMqKg0KDQpMb2dpc3RpYyByZWdyZXNzaW9uIGFpbXMgdG8gbW9kZWwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSAqKmJpbmFyeSoqIHJlc3BvbnNlIHZhcmlhYmxlIGFuZCBvdGhlciBmZWF0dXJlIHZhcmlhYmxlcyBieSBmaXR0aW5nIGEgbm9ubGluZWFyIGVxdWF0aW9uIChvZiBwcmVkaWN0b3IgZmVhdHVyZXMpIHRvIG9ic2VydmVkIGRhdGEuIFRoZSBnb2FsIGlzIHRvIGVpdGhlciBwcmVkaWN0IHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgJHkkIGJhc2VkIG9uIHRoZSB2YWx1ZXMgb2YgaW5kZXBlbmRlbnQgdmFyaWFibGVzICQoeF8xLCB4XzIsIFxjZG90cywgeF9rKSQgb3IgYXNzZXNzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiAkeSQgYW5kIHByZWRpY3RvciB2YXJpYWJsZXMgaW4gdGhlIGxpbmVhciBhbmQgbm9ubGluZWFyIGZvcm1zLg0KDQoNCiMjIyAqKlN0cnVjdHVyZSoqDQoNCkFzc3VtZSB0aGF0IHRoZSByZXNwb25zZSB2YXJpYWJsZSB0YWtlcyBjaGFyYWN0ZXIgdmFsdWVzIGAwYCBhbmQgYDFgIGFuZCAkUChZID0gIjEiKSQgaXMgdGhlIHByb2JhYmlsaXR5IG9mIG9ic2VydmluZyB0aGUgKipiaWdnZXIgdmFsdWUgb2YgdGhlIHJlc3BvbnNlKiogKGluIGFscGhhYmV0aWNhbCBvcmRlcikuIFRoZSBnZW5lcmFsIGZvcm0gb2YgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gaXMgZ2l2ZW4gYnkNCg0KJCQNClAoWT0xKSA9IFxmcmFje2Vee2YoeF8xLCB4XzIsIFxjZG90cywgeF9rKX19ezErZV57Zih4XzEsIHhfMiwgXGNkb3RzLCB4X2spfX0uDQokJA0KDQpUaGUgZGV0ZXJtaW5pc3RpYyBmdW5jdGlvbiAkZihcY2RvdCkkIGNhbiB0YWtlIGVpdGhlciBsaW5lYXIgb3IgcG9seW5vbWlhbCBleHByZXNzaW9ucyBvZiBwcmVkaWN0b3IgZmVhdHVyZSB2YXJpYWJsZXMuIEZvciBleGFtcGxlLCBpZiAkZih4XzEsIHhfMiwgXGNkb3RzLCB4X2spID0gXGJldGFfMCArIFxiZXRhXzEgeF8xICsgXGJldGFfMiB4XzIgKyBcY2RvdHMgKyBcYmV0YV9rIHhfayQsIHRoZSBleHBsaWNpdCBsb2dpc3RpYyBtb2RlbCBpcyBnaXZlbiBieQ0KDQokJA0KUChZPTEpID0gXGZyYWN7ZV57XGJldGFfMCArIFxiZXRhXzEgeF8xICsgXGJldGFfMiB4XzIgKyBcY2RvdHMgKyBcYmV0YV9rIHhfa319ezErZV57XGJldGFfMCArIFxiZXRhXzEgeF8xICsgXGJldGFfMiB4XzIgKyBcY2RvdHMgKyBcYmV0YV9rIHhfa319Lg0KJCQNCg0Kb3IgZXF1aXZhbGVudGx5DQoNCiQkDQpcbG9nIFxmcmFje1AoWT0xKX17MS1QKFk9MSl9ID0gXGJldGFfMCArIFxiZXRhXzEgeF8xICsgXGJldGFfMiB4XzIgKyBcY2RvdHMgKyBcYmV0YV9rIHhfay4NCiQkDQpUaGUgYWJvdmUgZm9ybXVsYXRpb24gb2YgdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgaGFzIGltcG9ydGFudCB0ZXJtczogKipUaGUgb2RkcyBvZiBvYnNlcnZpbmcgJFk9MSQqKiBpcyAkUChZPTEpL1sxLVAoWT0xKV0kLiBUaGUgbGVmdCBoYW5kIG9mIHRoZSBhYm92ZSBmb3JtdWxhdGlvbiBpcyBjYWxsZWQgbG9nYXJpdGhtaWMgb2RkcyBvZiBvYnNlcnZpbmcgJFk9MSQuIFRoYXQgaXMsDQokJA0KXHRleHR7bG9nIG9kZHMgb2YgKFkgPSAxKX0gPSBcbG9nIFxmcmFje1AoWT0xKX17MS1QKFk9MSl9Lg0KJCQNCg0KIyMjICoqQXNzdW1wdGlvbnMqKg0KDQpEaWZmZXJlbnQgbW9kZWxzIGhhdmUgZGlmZmVyZW50IGFzc3VtcHRpb25zLiBMb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscyBoYXZlIHRoZSBmb2xsb3dpbmcgYXNzdW1wdGlvbnMuDQoNCjx1bD4NCiogKipEYXRhIFNldCoqOiB0aGUgZGF0YSBtdXN0IGJlIElJRCBhbmQgdGhlIHNhbXBsZSBzaXplIG11c3QgYmUgbGFyZ2UuDQoNCiogKipTdHJ1Y3R1cmUqKjogdGhlIGZ1bmN0aW9uIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICRQKFk9MSkkIGFuZCB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBtdXN0IGJlIGNvcnJlY3RseSBzcGVjaWZpZWQuIFRoYXQgaXMsICRmKFxjZG90KSQgKiphcyBhIGZ1bmN0aW9uIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzKiogbXVzdCBiZSBjb3JyZWN0bHkgc3BlY2lmaWVkLg0KDQoqICoqZXJyb3IgRGlzdHJpYnV0aW9uKio6ICRZIFxzaW0gQmlub20ocCkkIHdoZXJlICRwID0gUChZPTEpJCBpcyBhIGJpbm9taWFsIGRpc3RyaWJ1dGlvbi4gPGZvbnQgY29sb3IgPSAicmVkIj4qKlxjb2xvcntyZWR9VGhlc2UgYXNzdW1wdGlvbnMgYXJlIGNydWNpYWwgdG8gZXN0aW1hdGUgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLCBkZWZpbmUgdmFsaWQgcC12YWx1ZXMgZm9yIHNpZ25pZmljYW5jZSB0ZXN0cyBvZiByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgYW5kIGNvbnN0cnVjdCBwcmVkaWN0aW9uIGludGVydmFscyB1c2luZyB0LWRpc3RyaWJ1dGlvbnMuKio8L2ZvbnQ+DQo8L3VsPg0KDQoNCiMjIFBhcmFtZXRlciBJbnRlcnByZXRhdGlvbg0KDQpJbiBwcmVkaWN0aXZlIGFuYWx5c2lzIGFuZCBjbGFzc2lmaWNhdGlvbiBhcHBsaWNhdGlvbnMsIHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudCBpcyBub3QgdGhlIGZvY3VzIGJ1dCB0aGUgYWNjdXJhY3kgb2YgdGhlIHVuZGVybHlpbmcgbW9kZWwgYW5kIGFsZ29yaXRobXMuIEhvd2V2ZXIsIGluIGFzc29jaWF0aW9uIGFuYWx5c2lzLCB0aGUgaW50ZXJwcmV0YXRpb24gb2YgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgaW4gdGhlIGZpbmFsIG1vZGVsIGlzIGNyaXRpY2FsIGZvciBlbnN1cmluZyB0aGF0IG1vZGVscyBhcmUgbm90IG9ubHkgY29ycmVjdCBidXQgYWxzbyByZWxpYWJsZSBhbmQgcmVzcG9uc2libGUgaW4gdGhlaXIgYXBwbGljYXRpb25zLiANCg0KVGhlIGludGVycHJldGFiaWxpdHkgb2YgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgaXMgZGVwZW5kZW50IG9uIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIHVuZGVybHlpbmcgcmVncmVzc2lvbiBtb2RlbCB3aGljaCBpcyBhbHNvIG9uZSBvZiB0aGUgY3JpdGVyaWEgZm9yIGFzc2Vzc2luZyB0aGUgZ29vZG5lc3Mgb2YgYSBtb2RlbC4gVGhpcyBzdWJzZWN0aW9uIGRpc2N1c3NlcyB0aGUgaW50ZXJwcmV0YXRpb24gb2YgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgb2YgbGluZWFyIGFuZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscy4NCg0KIyMjIExpbmVhciBSZWdyZXNzaW9uDQoNCldlIHByb3ZpZGUgYW4gaW50ZXJwcmV0YXRpb24gb2YgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgaW4gdGhyZWUgZGlmZmVyZW50IHNjZW5hcmlvcy4gDQoNCiogKipGaXJzdCBPcmRlciBMaW5lYXIgUmVncmVzc2lvbioqDQoNClJlY2FsbCB0aGF0IHRoZSBmaXJzdC1vcmRlciBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3aXRoICRrJCBwcmVkaWN0b3IgZmVhdHVyZSB2YXJpYWJsZSBoYXMgdGhlIGZvbGxvd2luZyBmb3JtLg0KDQokJA0KeSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfMSArIFxiZXRhXzIgeF8yICsgXGNkb3RzICsgXGJldGFfaSB4X2krIFxjZG90cyAgKyBcYmV0YV9rIHhfayArIFxlcHNpbG9uLCBcIFwgXHRleHR7IHdoZXJlIH0gXCBcIFxlcHNpbG9uIFxzaW0gTigwLCBcc2lnbWEpLg0KJCQNCndoZXJlICQxIFxsZSBpIFxsZSBrJCBhbmQgJFxlcHNpbG9uIFxzaW0gTigwLCBcc2lnbWEpJC4gTGV0DQoNCiQkDQp5XnsoaSwwKX0gPSBcYmV0YV8wICsgXGJldGFfMSB4XzEgKyBcYmV0YV8yIHhfMiArIFxjZG90cyArIFxiZXRhX2kgKHhfaSswKSsgXGNkb3RzICArIFxiZXRhX2sgeF9rICwNCiQkDQoNCmFuZA0KDQokJA0KeV57KGksMSl9ID0gXGJldGFfMCArIFxiZXRhXzEgeF8xICsgXGJldGFfMiB4XzIgKyBcY2RvdHMgKyBcYmV0YV9pICh4X2krMSkrIFxjZG90cyAgKyBcYmV0YV9rIHhfaywgDQokJA0KDQp0aGVuDQoNCiQkDQpcYmV0YV9pID0geV57KGksMSl9LSB5XnsoaSwwKX0uDQokJA0KDQpUaGUgY29lZmZpY2llbnQgb2YgJHhfaSQsIHJlcHJlc2VudHMgdGhlIGNoYW5nZSBpbiAkeSQgZm9yIGEgb25lLXVuaXQgaW5jcmVhc2UgaW4gJHhfaSQsIGhvbGRpbmcgYWxsIG90aGVyIHZhcmlhYmxlcyBjb25zdGFudC4gPGZvbnQgY29sb3I9InJlZCI+KipcY29sb3J7cmVkfVRoYXQgaXMsIHVuZGVyIHRoZSBzYW1lIGNvbmRpdGlvbiwgaWYgJHhfaSQgaW5jcmVhc2VzIG9uZS11bml0LCB0aGUgY29ycmVzcG9uZGluZyBjaGFuZ2UgaW4gJHkkIGlzIHRoZSBjb2VmZmljaWVudCAkXGJldGFfaSQuKio8L2ZvbnQ+DQoNCg0KKiAqKlBvbHlub21pYWwgUmVncmVzc2lvbnMgd2l0aCBubyBJbnRlcmFjdGlvbioqDQoNCldpdGhvdXQgbG9zcyBvZiBnZW5lcmFsaXR5LCB3ZSBjb25zaWRlciB0aGUgZm9sbG93aW5nIHBvbHlub21pYWwgcmVncmVzc2lvbiB3aXRoIG9uZSBwcmVkaWN0b3IgdmFyaWFibGUgJHgkLg0KDQokJA0KeSA9IFxhbHBoYV8wICsgXGFscGhhXzEgeCArIFxhbHBoYV8yIHheMiArIFxhbHBoYV8zIHheMyArIFxjZG90cyArIFxhbHBoYV9rIHheayArIFxlcHNpbG9uLCBcIFwgXHRleHR7IHdoZXJlIH0gXCBcIFxlcHNpbG9uIFxzaW0gTigwLCBcc2lnbWEpLg0KJCQNCg0KSW50ZXJwcmV0aW5nIHBvbHlub21pYWwgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgaW4gYSBwcmFjdGljYWwgc2Vuc2UgY2FuIGJlIGNoYWxsZW5naW5nLiBUaGUgY29lZmZpY2llbnRzIGluIGEgcG9seW5vbWlhbCByZWdyZXNzaW9uIG1vZGVsIGNvcnJlc3BvbmQgdG8gdGhlIHRlcm1zIG9mIHRoZSBwb2x5bm9taWFsIGVxdWF0aW9uIGFuZCByZWZsZWN0IHRoZSBiZWhhdmlvciBvZiB0aGUgY2hhbmdlIG9mIHRoZSBjdXJ2YXR1cmUgb2YgdGhlIChjdXJ2ZSBvZiB0aGUpIHBvbHlub21pYWwgZnVuY3Rpb24uIA0KDQpGcm9tIGEgcHJhY3RpY2FsIHBlcnNwZWN0aXZlLCB3ZSBzdWdnZXN0LCByYXRoZXIgdGhhbiBpbnRlcnByZXRpbmcgY29lZmZpY2llbnRzIGRpcmVjdGx5LCB1c2luZyBwbG90cyB0byB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICR4JCBhbmQgJHkkLg0KDQoqICoqUmVncmVzc2lvbiB3aXRoIDJuZCBPcmRlciBJbnRlcmFjdGlvbiBPbmx5KioNCg0KV2Ugd2lsbCBub3QgZGlzY3VzcyBoaWdoLW9yZGVyIGludGVyYWN0aW9uIHRlcm1zIGluIGFueSBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwuIFRoZSBmb2xsb3dpbmcgaXMgYW4gZW5kLW9yZGVyIHBvbHlub21pYWwgcmVncmVzc2lvbiBtb2RlbCB3aXRoIGFuIGludGVyYWN0aW9uIHRlcm0gb25seS4NCg0KJCQNCnkgPSBcZ2FtbWFfMCArIFxnYW1tYV8xIHhfMSArIFxnYW1tYV8yIHhfMiArIFxnYW1tYV97MTJ9eF8xeF8yICsgXGVwc2lsb24sIFwgXCBcdGV4dHsgd2hlcmUgfSBcIFwgXGVwc2lsb24gXHNpbSBOKDAsIFxzaWdtYSkNCiQkDQoNCldlIGZpeCAkeF8yJCBhbmQgaW5jcmVhc2UgJHhfMSQgYnkgb25lIHVuaXQsIHdlIGV2YWx1YXRlIHRoZSBjaGFuZ2Ugb2YgdGhlIHJlc3BvbnNlICR5JCBpbiB0aGUgZm9sbG93aW5nLg0KDQokJA0KeV57KHhfMSsxLHhfMil9IC0geV57KHhfMSx4XzIpfSA9IFtcZ2FtbWFfMCArIFxnYW1tYV8xICh4XzErMSkgKyBcZ2FtbWFfMiB4XzIgKyBcZ2FtbWFfezEyfSh4XzErMSl4XzJdIC1bXGdhbW1hXzAgKyBcZ2FtbWFfMSB4XzEgKyBcZ2FtbWFfMiB4XzIgKyBcZ2FtbWFfezEyfXhfMXhfMl09IFxnYW1tYV8xICsgXGdhbW1hX3sxMn14XzINCiQkDQpUaGUgY29lZmZpY2llbnQgJFxnYW1tYV97MTJ9JCBpbmRpY2F0ZXMgdGhlIGV4dGVudCB0byB3aGljaCB0aGUgZWZmZWN0IG9mICR4XzEkIG9uICR5JCBkZXBlbmRzIG9uIHRoZSB2YWx1ZSBvZiAkeF8yJCwgYW5kIHZpY2UgdmVyc2EuIEEgY29udGV4dC1zcGVjaWZpYyBleHBsYW5hdGlvbiBvZiB0aGUgaW50ZXJhY3Rpb24gaXMgZ2l2ZW4gaW4gdGhlIGZvbGxvd2luZyBhZ3JpY3VsdHVyYWwgZXhhbXBsZS4NCg0KQ29uc2lkZXIgYSBzdHVkeSBleGFtaW5pbmcgdGhlIGVmZmVjdHMgb2YgZmVydGlsaXplciBhbmQgd2F0ZXIgbGV2ZWxzIG9uIGNyb3AgeWllbGQuDQoNCiogJHkkOiBDcm9wIHlpZWxkIChpbiB0b25zIHBlciBoZWN0YXJlKS4NCg0KKiAkeF8xJDogQW1vdW50IG9mIGZlcnRpbGl6ZXIgYXBwbGllZCAoaW4ga2cgcGVyIGhlY3RhcmUpLg0KDQoqICR4XzIkOiBBbW91bnQgb2Ygd2F0ZXIgc3VwcGxpZWQgKGluIGxpdGVycyBwZXIgaGVjdGFyZSkuDQoNCkFzc3VtZSB0aGUgcmVncmVzc2lvbiBtb2RlbCBpcw0KDQokJA0KXHRleHR7Y3JvcH0gPSBcZ2FtbWFfMCArXGdhbW1hXzEgXHRleHR7d2F0ZXIgYW1vdW50fSsgXGdhbW1hXzJcdGltZXMgXHRleHR7ZmVydGlsaXplcn0gKyBcZ2FtbWFfezEyfSBcdGV4dHtmZXJ0aWxpemVyfVx0aW1lc1x0ZXh0e3dhdGVyIGFtb3VudH0uDQokJA0KDQo8Zm9udCBjb2xvciA9ICJyZWQiPioqXGNvbG9ye3JlZH1UaGUgaW50ZXJwcmV0YXRpb24qKjwvZm9udD46IHdoZW4gd2UgaW5jcmVhc2UgZmVydGlsaXplciBieSBvbmUga2cgYW5kIGtlZXAgdGhlIHNhbWUgYW1vdW50IG9mIHdhdGVyIHN1cHBseSwgdGhlIGluY3JlbWVudCBvZiB0aGUgY3JvcCB5aWVsZCBpcyBnaXZlbiBieQ0KDQokJA0KXHRleHR7VGhlIGluY3JlbWVudCBvZiBjcm9wIHlpZWxkfSA9IFxnYW1tYV8xICArIFxnYW1tYV97MTJ9IFx0aW1lcyBcdGV4dHt3YXRlciBhbW91bnQgKGtnKX0gDQokJA0KQSBwb3NpdGl2ZSAkXGdhbW1hX3sxMn0kIHN1Z2dlc3RzIHRoYXQgdGhlIGJlbmVmaXRzIG9mIGFkZGluZyBmZXJ0aWxpemVyICgkeF8xJCkgb24gY3JvcCB5aWVsZCBhcmUgYW1wbGlmaWVkIHdoZW4gd2F0ZXIgc3VwcGx5ICQoeF8yKSQgaXMgaGlnaC4gU2ltaWxhcmx5LCBhbiBpbmNyZWFzZWQgd2F0ZXIgc3VwcGx5IGlzIG1vcmUgYmVuZWZpY2lhbCB3aGVuIHN1ZmZpY2llbnQgZmVydGlsaXplciBpcyBhcHBsaWVkLiBUaGF0IGlzLCBkb3VibGluZyB3YXRlciBzdXBwbHkgd2hpbGUgZmVydGlsaXplciBsZXZlbHMgYXJlIGFscmVhZHkgaGlnaCBtYXkgbGVhZCB0byBtb3JlIHRoYW4gYSBwcm9wb3J0aW9uYWwgaW5jcmVhc2UgaW4geWllbGQuDQoNCkhlcmXigJlzIGFuIGV4YW1wbGUgaW4gUiB0byB2aWV3IHRoZSBpbnRlcmFjdGlvbiBlZmZlY3QgdXNpbmcgYSBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gd2l0aCBvbmUgdHdvLXdheSBpbnRlcmFjdGlvbiB0ZXJtLg0KDQoNCmBgYHtyfQ0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJ5DQojbGlicmFyeShnZ3Bsb3QyKQ0KIyBTaW11bGF0ZSBkYXRhDQpzZXQuc2VlZCgxMjMpICAjIEZvciByZXByb2R1Y2liaWxpdHkNCm4gPSAxMDANCngxID0gcnVuaWYobiwgMCwgMTApDQp4MiA9IHJ1bmlmKG4sIDAsIDEwKQ0KeiA9IDMgKyAyKngxIC0gMS41KngyICsgMC41KngxXjIgKyAwLjMqeDEqeDIgKyBybm9ybShuLCAwLCAzKQ0KDQojIENvbWJpbmUgaW50byBhIGRhdGEgZnJhbWUNCmRhdGEgPSBkYXRhLmZyYW1lKHgxID0geDEsIHgyID0geDIsIHkgPSB6KQ0KZGF0YSR4MXgyID0geDEqeDINCg0KIyBGaXQgYSBxdWFkcmF0aWMgcmVncmVzc2lvbiBtb2RlbCB3aXRoIGludGVyYWN0aW9uIHRlcm0NCm1vZGVsID0gbG0oeiB+IHgxICsgeDIgKyBJKHgxKngyKSwgZGF0YSA9IGRhdGEpDQojIFN1bW1hcnkgb2YgdGhlIG1vZGVsDQojc3VtbWFyeShtb2RlbCkNCmNvZWYxID0gY29lZihtb2RlbCkNCmRhdGEkejEgPSBjb2VmMVsxXSArIGNvZWYxWzJdKngxICsgY29lZjFbM10qeDIgKyBjb2VmMVs0XSpkYXRhJHgxeDIgDQojIw0KbW9kZWwwID0gbG0oen54MSt4MiwgZGF0YSA9IGRhdGEpDQpjb2VmMCA9IGNvZWYobW9kZWwwKQ0KZGF0YSR6MCA9IGNvZWYwWzFdICsgY29lZjBbMl0qeDEgKyBjb2VmMFszXSp4MiAgDQpgYGANCg0KYGBge3J9DQojIFZpc3VhbGl6aW5nIHRoZSBxdWFkcmF0aWMgcmVncmVzc2lvbiBzdXJmYWNlDQpxdWFkID0gcGxvdF9seShkYXRhLCB4PSB+eDEsIHk9IH54Miwgej0gfnoxLA0KICAgICAgICAgICAgICAgdHlwZT0nbWVzaDNkJywgDQogICAgICAgICAgICAgICBpbnRlbnNpdHkgPSB+ejEsDQogICAgICAgICAgICAgICB0ZXh0ID0gIkludGVyYWN0aW9uIiwNCiAgICAgICAgICAgICAgIGNvbG9ycz0gY29sb3JSYW1wKHJhaW5ib3coNSkpDQogICAgICAgICAgICkNCmxpbi5xdWFkID0gYWRkX3RyYWNlKHAgPSBxdWFkLA0KICAgICAgICAgICB6ID0gZGF0YSR6MCwNCiAgICAgICAgICAgeCA9IHgxLA0KICAgICAgICAgICB5ID0geDIsDQogICAgICAgICAgIHRleHQgPSAiTm8gSW50ZXJhY3Rpb24iLA0KICAgICAgICAgICB0eXBlID0gIm1lc2gzZCIpICU+JQ0KICAgICAgICAgICBoaWRlX2NvbG9yYmFyKCkgJT4lDQogICAgICAgICAgIGxheW91dCh0aXRsZSA9ICdQb2x5bm9taWFsIFJlZ3Jlc3Npb24gU3VyZmFjZTogTm8gSW50ZXJhY3Rpb24gdnMgSW50ZXJhY3Rpb24nLA0KICAgICAgICAgICAgICBtYXJnaW4gPSBsaXN0KGwgPSA1LCByID0gNSwgYiA9IDUwLCB0ID0gNTAsIHBhZCA9IDQpKQ0KbGluLnF1YWQNCmBgYA0KDQojIyMgTG9naXN0aWMgUmVncmVzc2lvbg0KDQpVbmxpa2UgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYXJlIGRpcmVjdGx5IHJlbGF0ZWQgdG8gdGhlIGNoYW5nZSBpbiB0aGUgcmVzcG9uc2UgdmFyaWFibGUuIEluIHRoZSBsb2dpc3RpYyByZWdyZXNzaW9uLCB0aGUgY2hhbmdlIG9mIHRoZSB2YWx1ZXMgb2YgcHJlZGljdG9yIGZlYXR1cmUgdmFyaWFibGVzIGluZmx1ZW5jZXMgdGhlIHByb2JhYmlsaXR5IG9mIG9ic2VydmluZyAkWT0xJC4gSWYgZXhwcmVzcyBsb2dpc3RpYyByZWdyZXNzaW9uIGFzIHRoZSBsb2dhcml0aG0gb2Ygb2RkcyBvZiBvYnNlcnZpbmcgJChZPTEpJCwgdGhhdCBpcywNCg0KJCQNClxsb2cgXHRleHR7T30gPVxsb2cgXGZyYWN7UChZPTEpfXsxLVAoWT0xKX0gPSBcYWxwaGFfMCArIFxhbHBoYV8xIHhfMSArIFxhbHBoYV8yIHhfMiArIFxjZG90cyArIFxhbHBoYV9rIHhfay4NCiQkDQoNCiRPID0gXHRleHR7T2RkcyBvZiBvYmVyc2VydmluZyBZPTF9JC4gVGhlbiB3ZSBjYW4gbWltaWMgdGhlIHdheSBvZiBpbnRlcnByZXRpbmcgdGhlIGNvZWZmaWNpZW50cyBpbiBsaW5lYXIgcmVncmVzc2lvbiwgdGhlIGktdGggcmVncmVzc2lvbiBjb2VmZmljaWVudCBpbiB0aGUgYWJvdmUgbXVsdGlwbGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBpcyBleHByZXNzZWQgYXMNCg0KJCQNClxhbHBoYV9pID0gXGxvZyBcdGV4dHtPfV57KHhfaSsxKX0gLSBcbG9nIFx0ZXh0e099XnsoeF9pKX0gPSBcbG9nIFxmcmFje1x0ZXh0e099XnsoeF9pKzEpfX17XHRleHR7T31eeyh4X2kpfX0NCiQkDQoNCldlIGV4cG9uZW50aWF0ZSBib3RoIHNpZGVzIG9mIHRoZSBhYm92ZSBlcXVhdGlvbiwgYW5kIHdlIGhhdmUNCg0KJCQNClxmcmFje1x0ZXh0e099XnsoeF9pKzEpfX17XHRleHR7T31eeyh4X2kpfX0gPSBlXntcYWxwaGFfaSB9LCBcIFwgXHRleHR7IHdoaWNoIGlzIGVxdWl2YWxlbnQgdG8gfVwgXCBcZnJhY3tcdGV4dHtPfV57KHhfaSsxKX0tXHRleHR7T31eeyh4X2kpfX17XHRleHR7T31eeyh4X2kpfX0gPSBlXntcYWxwaGFfaX0tMS4NCiQkDQpXZSByZS13cml0ZSB0aGUgbGFzdCBlcXVhdGlvbiBhcyB0aGUgZm9ybSBvZiB0aGUgcGVyY2VudGFnZSBjaGFuZ2UgaW4gdGhlIGZvbGxvd2luZy4NCg0KJCQNCjEwMFx0aW1lcyBcZnJhY3tcdGV4dHtPfV57KHhfaSsxKX0tXHRleHR7T31eeyh4X2kpfX17XHRleHR7T31eeyh4X2kpfX0gPSAxMDAoZV57XGFscGhhX2l9LTEpLg0KJCQNClRoZXJlZm9yZSwgd2UgaGF2ZSB0aGUgZm9sbG93aW5nIGludGVycHJldGF0aW9uIG9mIHRoZSAkXGFscGhhX2kkOiB1bmRlciB0aGUgc2FtZSBjb25kaXRpb24sIGluY3JlYXNpbmcgJHhfaSQgYnkgb25lIHVuaXQgbGVhZHMgdG8gdGhlIGNoYW5nZSBvZiBvZGRzIG9mIG9ic2VydmluZyAoWT0xKSBieSAkMTAwKGVee1xhbHBoYV9pfS0xKVwlJC4NCg0KVG8gaWxsdXN0cmF0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYSBwcmVkaWN0b3IgdmFyaWFibGUgYW5kIHRoZSByZXNwb25zZSB2YXJpYWJsZSB3aXRoIGEgMi13YXkgaW50ZXJhY3Rpb24sIHdlIGNhbiB1c2UgYSBncmFwaGljYWwgYXBwcm9hY2ggc3VjaCBhcyBpbnRlcmFjdGlvbiBwbG90cyBvciAzRCBzdXJmYWNlIHBsb3RzLiBUaGUgZm9sbG93aW5nIGV4YW1wbGUgdXNlcyBhIDNEIHN1cmZhY2UgcGxvdCB0byB2aWV3IHRoZSByZWxhdGlvbnNoaXAgaW4gdGhlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBhIDJuZCBvcmRlciBpbnRlcmFjdGlvbiBpbiB0aGUgZm9sbG93aW5nIGV4YW1wbGUgYmFzZWQgb24gc2ltdWxhdGVkIGRhdGEuDQoNCg0KDQpgYGB7cn0NCiMgTG9hZCBuZWNlc3NhcnkgbGlicmFyeQ0KbGlicmFyeShnZ3Bsb3QyKQ0KIyBTaW11bGF0ZSBhIHdvcmtpbmcgZGF0YSBzZXQNCnNldC5zZWVkKDEyMykgICMgRm9yIHJlcHJvZHVjaWJpbGl0eQ0KbiA9IDEwMA0KeDEgPSBybm9ybShuLCAwLjQsMikNCngyID0gcmV4cChuLCAxLjIpDQpsMCA9LjEgKyAuMip4MSAtIDEuNSp4MiAgKyAwLjUqeDEqeDINCnAgPSBleHAobDApLygxK2V4cChsMCkpDQp6ID0gaWZlbHNlKHA+MC40OCwgMSwgMCkNCiMgQ29tYmluZSBpbnRvIGEgZGF0YSBmcmFtZQ0KZGF0YSA9IGRhdGEuZnJhbWUoeDEgPSB4MSwgeDIgPSB4MiwgeSA9IHopDQojIEZpdCBhIHF1YWRyYXRpYyByZWdyZXNzaW9uIG1vZGVsIHdpdGggaW50ZXJhY3Rpb24gdGVybQ0KbG9naXQwID0gZ2xtKHkgfiB4MSArIHgyLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rPWxvZ2l0KSwgZGF0YSA9IGRhdGEpDQojIFN1bW1hcnkgb2YgdGhlIG1vZGVsDQojc3VtbWFyeShsb2dpdDApDQpjb2VmMCA9IGNvZWYobG9naXQwKQ0KbG4wID0gY29lZjBbMV0gKyBjb2VmMFsyXSp4MSArIGNvZWYwWzNdKngyIA0KZGF0YSR6MCA9IGV4cChsbjApLygxK2V4cChsbjApKSAgDQojIyMNCmxvZ2l0MSA9IGxtKHp+eDEreDIreDEqeDIsZmFtaWx5ID0gYmlub21pYWwobGluaz1sb2dpdCksIGRhdGEgPSBkYXRhKQ0KI3N1bW1hcnkobG9naXQxKQ0KY29lZjEgPSBjb2VmKGxvZ2l0MSkNCmxuMSA9IGNvZWYxWzFdICsgY29lZjFbMl0qeDEgKyBjb2VmMVszXSp4MiArIGNvZWYxWzRdKmRhdGEkeDEqeDIgDQpkYXRhJHoxID0gZXhwKGxuMSkvKDErZXhwKGxuMSkpICANCmBgYA0KDQoNCmBgYHtyfQ0KIyBWaXN1YWxpemluZyB0aGUgcXVhZHJhdGljIHJlZ3Jlc3Npb24gc3VyZmFjZQ0KI2xpYnJhcnkoZ2dwbG90MikNCiNsaWJyYXJ5KHBsb3RfbHkpDQpxdWFkID0gcGxvdF9seShkYXRhLA0KICAgICAgICAgICAgICAgeD0gfngxLCANCiAgICAgICAgICAgICAgIHk9IH54MiwgDQogICAgICAgICAgICAgICB6PSB+ejEsDQogICAgICAgICAgICAgICB0eXBlPSdtZXNoM2QnLCANCiAgICAgICAgICAgICAgIGludGVuc2l0eSA9IH56MSwNCiAgICAgICAgICAgICAgIHRleHQgPSAiSW50ZXJhY3Rpb24iLA0KICAgICAgICAgICAgICAgY29sb3JzPSBjb2xvclJhbXAodG9wby5jb2xvcnMoNSwgYWxwaGE9MC43LCByZXYgPSBUUlVFKSkNCiAgICAgICAgICAgKSANCnF1YWQubG4gPSBhZGRfdHJhY2UoDQogICAgICAgICAgIHAgPSBxdWFkLA0KICAgICAgICAgICB6ID0gfnowLA0KICAgICAgICAgICB4ID0gfngxLA0KICAgICAgICAgICB5ID0gfngyLA0KICAgICAgICAgICB0ZXh0ID0gIk5vIEludGVyYWN0aW9uIiwNCiAgICAgICAgICAgdHlwZSA9ICJtZXNoM2QiLA0KICAgICAgICAgICBpbnRlbnNpdHkgPSB+ejAsDQogICAgICAgICAgIGNvbG9ycz0gY29sb3JSYW1wKHJhaW5ib3coNSkpKSAlPiUNCiAgICAgICAgICAgaGlkZV9jb2xvcmJhcigpICU+JQ0KICAgICAgIGxheW91dCh0aXRsZSA9ICdMb2dpc3RpYyBTdXJmYWNlOiBObyBJbnRlcmFjdGlvbiB2cyBJbnRlcmFjdGlvbicsDQogICAgICAgICAgICAgIG1hcmdpbiA9IGxpc3QobCA9IDUsIHIgPSA1LCBiID0gNTAsIHQgPSA1MCwgcGFkID0gNCkpDQpxdWFkLmxuDQpgYGANCg0KIyMgUGFyZW1ldGVyIEVzdGltYXRpb24gDQoNClRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBpbiBib3RoIChub3JtYWwpIGxpbmVhciBhbmQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHMgYXJlIGVzdGltYXRlZCBieSBtYXhpbWl6aW5nIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzIC0gTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24gKE1MRSkuDQoNCiMjIyBDb25jZXB0cyBvZiBMaWtlbGlvb2QgPGZvbnQgY29sb3IgPSAicmVkIj4qXGNvbG9ye3JlZH0oT3B0aW9uYWwpKjwvZm9udD4NCg0KRmlzaGVyJ3MgTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24gKE1MRSkgaXMgYSBtZXRob2QgZm9yIGVzdGltYXRpbmcgdGhlIHBhcmFtZXRlcnMgb2YgYSBzdGF0aXN0aWNhbCBtb2RlbCB0aGF0IG1heGltaXplcyB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbi4gVGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gcmVwcmVzZW50cyB0aGUgcHJvYmFiaWxpdHkgb2YgdGhlIG9ic2VydmVkIGRhdGEgZ2l2ZW4gdGhlIHBhcmFtZXRlcnMgb2YgdGhlIG1vZGVsLiBGaXNoZXIncyBNTEUgaXMgd2lkZWx5IHVzZWQgYmVjYXVzZSBpdCBoYXMgZGVzaXJhYmxlIHByb3BlcnRpZXMgdW5kZXIgY2VydGFpbiBjb25kaXRpb25zLCBzdWNoIGFzIGNvbnNpc3RlbmN5LCBlZmZpY2llbmN5LCBhbmQgYXN5bXB0b3RpYyBub3JtYWxpdHkuDQoNCkluIGdlbmVyYWwsIGxldCAkXHt4XzEsIHhfMiwgXGNkb3RzLCB4X24gXH0gXHN0YWNrcmVse1x0ZXh0e2kuaS5kfX1cc2ltIGYoeDsgXGFscGhhLCBcYmV0YSkkLCB0aGVuIDxmb250IGNvbG9yID0gInJlZCI+KipcY29sb3J7cmVkfXRoZSBsaWtlbGlob29kIG9mIG9ic2VydmluZyB0aGUgcmFuZG9tIHNhbXBsZSoqPC9mb250PiBpcyBkZWZpbmVkIHRvIGJlDQoNCiQkDQpcbWF0aGJie0x9KFxhbHBoYSwgXGJldGEpID0gXHByb2Rfe2k9MX1ebiBmKHhfaTsgXGFscGhhLCBcYmV0YSkuDQokJA0KRmlzaGVyJ3MgcHJvcG9zYWwgb2YgZXN0aW1hdGluZyBwYXJhbWV0ZXJzICQoXGFscGhhLCBcYmV0YSkkIGlzIHRvIGZpbmQgdGhlIHZhbHVlcyBmb3IgJFxhbHBoYSQgYW5kICRcYmV0YSQsIGRlbm90ZWQgYnkgJChcaGF0e1xhbHBoYX0sIFxoYXR7XGJldGF9KSQsIHRoYXQgbWF4aW1pemUgdGhlIGxpa2VsaWhvb2QgJFxtYXRoYmJ7TH0oXGFscGhhLCBcYmV0YSkkLiBXZSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgbm90YXRpb24gdG8gZGVub3RlIHRoZSBhYm92ZSBvcHRpbWl6YXRpb24gDQoNCiQkDQooXGhhdHtcYWxwaGF9LCBcaGF0e1xiZXRhfSkgPSBcYXJnXG1heF97KFxhbHBoYSwgXGJldGEpfSBcbWF0aGJie0x9KFxhbHBoYSwgXGJldGEpDQokJA0KDQpGb3IgY29tcHV0YXRpb25hbCBjb252ZW5pZW5jZSwgSW5zdGVhZCBvZiBtYXhpbWl6aW5nIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uICRcbWF0aGJie0x9KFxhbHBoYSwgXGJldGEpJCwgd2UgbWF4aW1pemUgdGhlIGxvZy1saWtlbGlob29kICRcbWF0aGNhbHtsfShcYWxwaGEsIFxiZXRhKSA9IFxsb2cgXG1hdGhiYntMfShcYWxwaGEsIFxiZXRhKSQuIFRoaXMgY29udmVydHMgdGhlIHByb2R1Y3Qgb2YgcHJvYmFiaWxpdGllcyBpbnRvIGEgc3VtLCBzaW1wbGlmeWluZyBjYWxjdWxhdGlvbnMgYW5kIHJlZHVjaW5nIG51bWVyaWNhbCBpbnN0YWJpbGl0eS4gVGhhdCBpcywNCg0KJCQNCihcaGF0e1xhbHBoYX0sIFxoYXR7XGJldGF9KSA9IFxhcmdcbWF4X3soXGFscGhhLCBcYmV0YSl9IFxtYXRoYmJ7bH0oXGFscGhhLCBcYmV0YSkNCiQkDQp3aGVyZQ0KDQokJA0KXG1hdGhiYntsfShcYWxwaGEsIFxiZXRhKSA9IFxzdW1fe2k9MX1ebiBcbG9nIGYoeF9pOyBcYWxwaGEsIFxiZXRhKS4NCiQkDQoNCiMjIyBOb3JtYWwgTGluZWFyIFJlZ3Jlc3Npb24gTGlrZWxpaG9vZCA8Zm9udCBjb2xvciA9ICJyZWQiPipcY29sb3J7cmVkfShPcHRpb25hbCkqPC9mb250Pg0KDQpXaXRob3V0IGxvc3Mgb2YgZ2VuZXJhbGl0eSwgd2UgY29uc2lkZXIgdGhlIGZvbGxvd2luZyBub3JtYWwgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwuDQoNCiQkDQpZID0gXGFscGhhXzAgKyBcYWxwaGFfMSB4XzEgKyBcYWxwaGFfMiB4XzIgKyBcY2RvdHMgKyBcYWxwaGFfayB4X2sgKyBcZXBzaWxvbiwgXCBcIFx0ZXh0eyB3aGVyZSB9IFwgXCBcZXBzaWxvbiBcc2ltIE4oMCwgXHNpZ21hKQ0KJCQNCldoaWNoIGltcGxpZXMgdGhhdCANCg0KJCQNClxtdSA9IEVbWV0gPSBcYWxwaGFfMCArIFxhbHBoYV8xIHhfMSArIFxhbHBoYV8yIHhfMiArIFxjZG90cyArIFxhbHBoYV9rIHhfayBcIFwgXHRleHR7IGFuZCB9IFwgXCBcdGV4dHt2YXJ9KFkpID0gXHNpZ21hXjIuDQokJA0KDQpPciBlcXVpdmFsZW50IHRvDQoNCiQkDQpZIFxzaW0gTihcbXUsIFxzaWdtYSkuDQokJA0KDQpUaGUgcHJlZGljdG9yIGZlYXR1cmUgdmFyaWFibGVzICR4XzEsIHhfMiwgXGNkb3RzLCB4X2skIGFyZSBhc3N1bWVkIHRvIGJlIG5vbi1yYW5kb20gYW5kIHRoZSByZXNwb25zZSB2YXJpYWJsZSAkeSQgaXMgYSByYW5kb20gdmFyaWFibGUgd2l0aCB0aGUgYWJvdmUgZGlzdHJpYnV0aW9uLiBUaGUgZXhwbGljaXQgZGVuc2l0eSBvZiAkWSQgaXMgZ2l2ZW4gYnkNCg0KJCQNCmYoeSkgPSBcZnJhY3sxfXtcc3FydHsyXHBpfVxzaWdtYX0gZV57LVxmcmFje1t5LShcYWxwaGFfMCArIFxhbHBoYV8xIHhfMSArIFxhbHBoYV8yIHhfMiArIFxjZG90cyArIFxhbHBoYV9rIHhfayldXjJ9ezJcc2lnbWFeMn19Lg0KJCQNCg0KTGV0ICRceyh5XzEsIHhfezFpfSwgeF97Mml9LCBcY2RvdHMsIHhfe2tpfSlcfV97aT0xfV5uIFxzdGFja3JlbHtcdGV4dHtpLmkuZH19XHNpbSAgZih5KSQsIHRoZSBsaWtlbGlob29kIG9mIG9ic2VydmluZyB0aGUgYGkuaS5kYCBzYW1wbGUgYmFzZWQgb24gdGhlIGFib3ZlIGRpc3RyaWJ1dGlvbmFsIGFzc3VtcHRpb24gaXMgZ2l2ZW4gYnkNCg0KJCQNClxtYXRoYmJ7TH0oXGFscGhhXzAsIFxhbHBoYV8xLCBcY2RvdHMsIFxhbHBoYV9rKSA9IChcc3FydHsyXHBpfVxzaWdtYSleey1ufWVeey1cc3VtX3tpPTF9Xm4gXGZyYWN7W3lfaS0oXGFscGhhXzAgKyBcYWxwaGFfezF9IHhfezFpfSArIFxhbHBoYV97Mn0geF97Mml9ICsgXGNkb3RzICsgXGFscGhhX2sgeF97a2l9KV1eMn17MlxzaWdtYV4yfX0NCiQkDQpUaGUgbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb24gaXMNCg0KJCQNClxtYXRoYmJ7bH0oXGFscGhhXzAsIFxhbHBoYV8xLCBcY2RvdHMsIFxhbHBoYV9rKT0tblxsb2coXHNxcnR7MlxwaX1cc2lnbWEpLVxmcmFjezF9ezJcc2lnbWFeMn1cc3VtX3tpPTF9Xm4gW3lfaS0oXGFscGhhXzAgKyBcYWxwaGFfezF9IHhfezFpfSArIFxhbHBoYV97Mn0geF97Mml9ICsgXGNkb3RzICsgXGFscGhhX2sgeF97a2l9KV1eMg0KJCQNCg0KVGhlIE1MRSBpcyB0aGUgc29sdXRpb24gdG8gdGhlIGFib3ZlIG9wdGltaXphdGlvbiBwcm9ibGVtLg0KDQoNCiMjIyBMb2dpc3RpYyBSZWdyZXNzaW9uIExpa2VsaWhvb2QgPGZvbnQgY29sb3IgPSAicmVkIj4qXGNvbG9ye3JlZH0oT3B0aW9uYWwpKjwvZm9udD4NCg0KTm90ZSB0aGF0IHRoZSByZXNwb25zZSB2YXJpYWJsZSBpbiB0aGUgYmluYXJ5IGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgaXMgYXNzdW1lZCB0byBiZSBhIEJlcm5vdWxsaSByYW5kb20gdmFyaWFibGUgd2l0aCBgc3VjY2Vzc2AgcHJvYmFiaWxpdHkgJHAkIHRoYXQgaXMgZGVmaW5lZCBhcyANCg0KJCQNCnAgPSBcZnJhY3tcZXhwKFxhbHBoYV8wICsgXGFscGhhXzEgeF8xICsgXGFscGhhXzIgeF8yICsgXGNkb3RzICsgXGFscGhhX2sgeF9rKX17MSArIFxleHAoXGFscGhhXzAgKyBcYWxwaGFfMSB4XzEgKyBcYWxwaGFfMiB4XzIgKyBcY2RvdHMgKyBcYWxwaGFfayB4X2spfQ0KJCQNCg0Kd2l0aCBwcm9iYWJpbGl0eSBtYXNzIGZ1bmN0aW9uDQoNCiQkDQpQKFkgPSB5KSA9IHBeeSgxLXApXnsxLXl9LCBcIFwgXHRleHR7IHdoZXJlIH0gXCBcIHkgPSAwIFwgXHRleHR7IG9yIH0gXCAxLg0KJCQNCg0KTGV0ICRceyh5XzEsIHhfezFpfSwgeF97Mml9LCBcY2RvdHMsIHhfe2tpfSlcfV97aT0xfV5uIFxzdGFja3JlbHtcdGV4dHtpLmkuZH19XHNpbSAgZih5KSQsIHRoZSBsaWtlbGlob29kIG9mIG9ic2VydmluZyB0aGUgYGkuaS5kYCBzYW1wbGUgYmFzZWQgb24gdGhlIGFib3ZlIGRpc3RyaWJ1dGlvbmFsIGFzc3VtcHRpb24gaXMgZ2l2ZW4gYnkNCg0KJCQNClxtYXRoYmJ7TH0oXGFscGhhXzAsIFxhbHBoYV8xLCBcY2RvdHMsIFxhbHBoYV9rKT1ccHJvZF97aT0xfV5uIHBee3lfaX0oMS1wKV57MS15X2l9Lg0KJCQNCg0KVGhlIGNvcnJlc3BvbmRpbmcgbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb24gaXMgZ2l2ZW4gYnkNCg0KJCQNClxtYXRoYmJ7bH0oXGFscGhhXzAsIFxhbHBoYV8xLCBcY2RvdHMsIFxhbHBoYV9rKT1cc3VtX3tpPTF9Xm5beV9pXGxvZyBwICsoMS15X2kpXGxvZyAoMS1wKV0NCiQkDQoNCiQkDQo9IFxzdW1fe2k9MX1ebiB5X2lcbGVmdFtcZnJhY3tcZXhwKFxhbHBoYV8wICsgXGFscGhhX3sxfSB4X3sxaX0gKyBcYWxwaGFfezJ9IHhfezJpfSArIFxjZG90cyArIFxhbHBoYV9rIHhfe2tpfSl9ezErXGV4cChcYWxwaGFfMCArIFxhbHBoYV97MX0geF97MWl9ICsgXGFscGhhX3syfSB4X3syaX0gKyBcY2RvdHMgKyBcYWxwaGFfayB4X3traX0pfSBccmlnaHRdXGxlZnRbMS0gXGZyYWN7XGV4cChcYWxwaGFfMCArIFxhbHBoYV97MX0geF97MWl9ICsgXGFscGhhX3syfSB4X3syaX0gKyBcY2RvdHMgKyBcYWxwaGFfayB4X3traX0pfXsxK1xleHAoXGFscGhhXzAgKyBcYWxwaGFfezF9IHhfezFpfSArIFxhbHBoYV97Mn0geF97Mml9ICsgXGNkb3RzICsgXGFscGhhX2sgeF97a2l9KX1ccmlnaHRdLg0KJCQNCg0KVGhlIE1MRSBvZiB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgbWF4aW1pemVzIHRoZSBhYm92ZSBsb2ctbGlrZWxpaG9vZC4gVG8gYmUgbW9yZSBzcGVjaWZpYywNCg0KJCQNCihcaGF0e1xhbHBoYX1fMCwgXGhhdHtcYWxwaGF9XzEsIFxjZG90cywgXGhhdHtcYWxwaGF9X2spID0gXGFyZ1xtYXhfeyhcYWxwaGFfMCwgXGFscGhhXzEsIFxjZG90cywgXGFscGhhX2spfSBcbWF0aGJie0x9KFxhbHBoYV8wLCBcYWxwaGFfMSwgXGNkb3RzLCBcYWxwaGFfaykuDQokJA0KDQoNCiMjIyBGaW5kaW5nIE1MRSBVc2luZyBTb2Z0d2FyZSBQcm9ncmFtcyANCg0KSW4gUiwgdGhlcmUgYXJlIHNldmVyYWwgbGlicmFyaWVzIGZvciBwZXJmb3JtaW5nIGxpbmVhciBhbmQgbG9naXN0aWMgcmVncmVzc2lvbiwgZWFjaCB3aXRoIHVuaXF1ZSBzdHJlbmd0aHMgZm9yIGRpZmZlcmVudCB1c2UgY2FzZXMuIFdlIHdpbGwgdXNlIHRoZSBwb3B1bGFyIFIgYnVpbHQtaW4gZGF0YSBzZXQgdGhhdCB3YXMgZXh0cmFjdGVkIGZyb20gdGhlIDE5NzQgTW90b3IgVHJlbmQgVVMgbWFnYXppbmUgYW5kIGNvbXByaXNlcyBmdWVsIGNvbnN1bXB0aW9uIGFuZCAxMCBhc3BlY3RzIG9mIGF1dG9tb2JpbGUgZGVzaWduIGFuZCBwZXJmb3JtYW5jZSBmb3IgMzIgYXV0b21vYmlsZXMgKDE5NzMtNzQgbW9kZWxzKS4NCg0KKiAqKm1wZyoqCU1pbGVzLyhVUykgZ2FsbG9uDQoqICoqY3lsKioJTnVtYmVyIG9mIGN5bGluZGVycw0KKiAqKmRpc3AqKiBEaXNwbGFjZW1lbnQgKGN1LmluLikNCiogKipocCoqIEdyb3NzIGhvcnNlcG93ZXINCiogKipkcmF0KiogUmVhciBheGxlIHJhdGlvDQoqICoqd3QqKglXZWlnaHQgKDEwMDAgbGJzKQ0KKiAqKnFzZWMqKiAxLzQgbWlsZSB0aW1lDQoqICoqdnMqKiBFbmdpbmUgKDAgPSBWLXNoYXBlZCwgMSA9IHN0cmFpZ2h0KQ0KKiAqKmFtKiogVHJhbnNtaXNzaW9uICgwID0gYXV0b21hdGljLCAxID0gbWFudWFsKQ0KKiAqKmdlYXIqKglOdW1iZXIgb2YgZm9yd2FyZCBnZWFycw0KDQoqKjEuIEJhc2UgUiAoc3RhdHMgcGFja2FnZSkqKg0KDQoqIExpbmVhciBSZWdyZXNzaW9uDQoNCmBgYHtyfQ0KIyBMb2FkIHRoZSBkYXRhDQoNCg0KIyBGaXQgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbA0KbW9kZWxfbG0gPC0gbG0obXBnIH4gd3QgKyBocCwgZGF0YSA9IG10Y2FycykNCg0KIyBTdW1tYXJ5IG9mIHRoZSBtb2RlbA0Kc3VtbWFyeShtb2RlbF9sbSkNCg0KIyBQcmVkaWN0IG5ldyB2YWx1ZXMNCnByZWRpY3QobW9kZWxfbG0sIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHd0ID0gMywgaHAgPSAxMDApKQ0KYGBgDQoNCiogTG9naXN0aWMgUmVncmVzc2lvbg0KDQpgYGB7cn0NCiMgTG9hZCB0aGUgZGF0YQ0KZGF0YShtdGNhcnMpDQptdGNhcnMkYW0gPC0gYXMuZmFjdG9yKG10Y2FycyRhbSkgIyBDb252ZXJ0IHRvIGZhY3RvciBmb3IgbG9naXN0aWMgcmVncmVzc2lvbg0KDQojIEZpdCBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCm1vZGVsX2dsbSA8LSBnbG0oYW0gfiB3dCArIGhwLCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IG10Y2FycykNCg0KIyBTdW1tYXJ5IG9mIHRoZSBtb2RlbA0Kc3VtbWFyeShtb2RlbF9nbG0pDQoNCiMgUHJlZGljdCBwcm9iYWJpbGl0aWVzDQpwcmVkaWN0KG1vZGVsX2dsbSwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUod3QgPSAzLCBocCA9IDEwMCksIHR5cGUgPSAicmVzcG9uc2UiKQ0KYGBgDQoNCg0KKioyLiBNQVNTIFBhY2thZ2UqKjogUm9idXN0IExpbmVhciBSZWdyZXNzaW9uDQoNCg0KYGBge3J9DQojbGlicmFyeShNQVNTKQ0KDQojIFJvYnVzdCBsaW5lYXIgcmVncmVzc2lvbg0KbW9kZWxfcmxtIDwtIHJsbShtcGcgfiB3dCArIGhwLCBkYXRhID0gbXRjYXJzKQ0KDQojIFN1bW1hcnkNCnN1bW1hcnkobW9kZWxfcmxtKQ0KYGBgDQoNCg0KKiozLiBnbG1uZXQgUGFja2FnZSoqOiBQZW5hbGl6ZWQgUmVncmVzc2lvbiAoTGFzc28gYW5kIFJpZGdlKQ0KDQpgYGB7cn0NCiNsaWJyYXJ5KGdsbW5ldCkNCg0KIyBQcmVwYXJlIGRhdGENClggPC0gYXMubWF0cml4KG10Y2Fyc1ssIGMoInd0IiwgImhwIildKQ0KeSA8LSBtdGNhcnMkbXBnDQoNCiMgRml0IGEgcmlkZ2UgcmVncmVzc2lvbiBtb2RlbCAoYWxwaGEgPSAwKQ0KbW9kZWxfcmlkZ2UgPC0gZ2xtbmV0KFgsIHksIGFscGhhID0gMCkNCg0KIyBGaXQgYSBsYXNzbyByZWdyZXNzaW9uIG1vZGVsIChhbHBoYSA9IDEpDQptb2RlbF9sYXNzbyA8LSBnbG1uZXQoWCwgeSwgYWxwaGEgPSAxKQ0KDQojIENyb3NzLXZhbGlkYXRpb24gZm9yIGxhbWJkYQ0KY3ZfbW9kZWwgPC0gY3YuZ2xtbmV0KFgsIHksIGFscGhhID0gMSkNCg0KIyBCZXN0IGxhbWJkYQ0KY3ZfbW9kZWwkbGFtYmRhLm1pbg0KDQojIFByZWRpY3QgdXNpbmcgdGhlIGxhc3NvIG1vZGVsDQpwcmVkaWN0KG1vZGVsX2xhc3NvLCBuZXd4ID0gYXMubWF0cml4KGRhdGEuZnJhbWUod3QgPSAzLCBocCA9IDEwMCkpLCBzID0gY3ZfbW9kZWwkbGFtYmRhLm1pbikNCmBgYA0KDQoqKjQuIGNhcmV0IFBhY2thZ2UqKjogVW5pZmllZCBJbnRlcmZhY2UgZm9yIFRyYWluaW5nIE1vZGVscw0KDQpgYGB7cn0NCiNsaWJyYXJ5KGNhcmV0KQ0KDQojIExpbmVhciByZWdyZXNzaW9uDQptb2RlbF9jYXJldF9sbSA8LSB0cmFpbihtcGcgfiB3dCArIGhwLCBkYXRhID0gbXRjYXJzLCBtZXRob2QgPSAibG0iKQ0Kc3VtbWFyeShtb2RlbF9jYXJldF9sbSkNCg0KIyBMb2dpc3RpYyByZWdyZXNzaW9uDQptb2RlbF9jYXJldF9nbG0gPC0gdHJhaW4oYW0gfiB3dCArIGhwLCBkYXRhID0gbXRjYXJzLCBtZXRob2QgPSAiZ2xtIiwgZmFtaWx5ID0gYmlub21pYWwpDQpzdW1tYXJ5KG1vZGVsX2NhcmV0X2dsbSkNCmBgYA0KDQoNCg0KDQoNCg0KIyBQZXJmb3JtYW5jZSBNZWFzdXJlcw0KDQpQZXJmb3JtYW5jZSBtZWFzdXJlcyBhcmUgbWV0cmljcyB1c2VkIHRvIGFzc2VzcyB0aGUgcXVhbGl0eSBvZiBzdGF0aXN0aWNzIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWxzLiBUaGV5IHF1YW50aWZ5IGhvdyB3ZWxsIGEgbW9kZWwgbWFrZXMgcHJlZGljdGlvbnMsIGNhcHR1cmluZyBpdHMgc3RyZW5ndGhzIGFuZCBsaW1pdGF0aW9ucy4gVGhlIGNob2ljZSBvZiBwZXJmb3JtYW5jZSBtZWFzdXJlcyBkZXBlbmRzIG9uIHRoZSBwcm9ibGVtIGJlaW5nIGFkZHJlc3NlZOKAlGNsYXNzaWZpY2F0aW9uLCByZWdyZXNzaW9uLCBjbHVzdGVyaW5nLCBldGMuDQoNCkFzIHByZXZpb3VzbHkgbWVudGlvbmVkLCBtb2RlbGluZyBpcyBhbiBpdGVyYXRpdmUgcHJvY2VzcyB0aGF0IGluY2x1ZGVzIGZlYXR1cmUgZW5naW5lZXJpbmcsIG1vZGVsIGNvbXBhcmlzb24sIHZhbGlkYXRpb24sIGFuZCB0ZXN0aW5nIGJhc2VkIG9uIHNwZWNpZmljIHBlcmZvcm1hbmNlIG1lYXN1cmVzLiBXZSBkZWRpY2F0ZSBhIHN0YW5kYWxvbmUgc2VjdGlvbiB0byB2YXJpb3VzIG1ldHJpY3MuDQoNCk5leHQsIHdlIHN1bW1hcml6ZSBkaWZmZXJlbnQgcGVyZm9ybWFuY2UgbWV0cmljcyBiYXNlZCBvbiB0aGUgYW5hbHl0aWMgdGFza3MgYXNzb2NpYXRlZCB3aXRoIHZhcmlvdXMgbW9kZWxzIGFuZCBhbGdvcml0aG1zLiBTb21lIGV4YW1wbGVzIGZyb20gdGhlIGxhc3Qgc3Vic2VjdGlvbiBvZiB0aGUgcHJldmlvdXMgc2VjdGlvbiBhcmUgdXNlZCB0byBpbGx1c3RyYXRlIHRoZSBjYWxjdWxhdGlvbiBvZiB0aGVzZSBwZXJmb3JtYW5jZSBtZXRyaWNzLg0KDQoNCiMjIFJlZ3Jlc3Npb24gUGVyZm9ybWFuY2UgTWV0cmljcw0KDQpXZSBsaXN0IHRoZSBmb2xsb3dpbmcgY29tbW9ubHkgdXNlZCBwZXJmb3JtYW5jZSBtZXRyaWNzIGZvciByZWdyZXNzaW9uIG1vZGVsaW5nLg0KDQoqICoqTWVhbiBBYnNvbHV0ZSBFcnJvciAoTUFFKSoqOiBSZXByZXNlbnRzIHRoZSBhdmVyYWdlIGFic29sdXRlIGRpZmZlcmVuY2UgYmV0d2VlbiBwcmVkaWN0ZWQgYW5kIGFjdHVhbCB2YWx1ZXMgZm9yIHJlZ3Jlc3Npb24gbW9kZWxzIHdpdGggbnVtZXJpY2FsIHJlc3BvbnNlLiBUaGUgZm9sbG93aW5nIGV4YW1wbGUgc2hvd3MgdGhlIGNhbGN1bGF0aW9uIG9mIE1BRS4NCg0KDQpgYGB7cn0NCiMgZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24NCmxuLm1vZGVsIDwtIGxtKG1wZyB+IHd0ICsgaHAsIGRhdGEgPSBtdGNhcnMpDQojIGZpdHRlZC9wcmVkaWN0ZWQgdmFsdWVzDQpmaXQudmFsIDwtIGZpdHRlZChsbi5tb2RlbCkNCiMgTUFFDQpNQUUgPSBtZWFuKGFicyhmaXQudmFsLW10Y2FycyRtcGcpKQ0KTUFFDQpgYGANCg0KKiAqKk1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBhbmQgUm9vdCBNZWFuIFNxdWFyZWQgRXJyb3IgKFJNU0UpKio6IE1TRSBwZW5hbGl6ZXMgbGFyZ2VyIGVycm9ycyBtb3JlIGhlYXZpbHksIHdoaWxlIFJNU0Ugb2ZmZXJzIGludGVycHJldGFiaWxpdHkgaW4gdGhlIHNhbWUgdW5pdHMgYXMgdGhlIHRhcmdldCB2YXJpYWJsZS4gDQoNCmBgYHtyfQ0KIyBmaXQgYSBsaW5lYXIgcmVncmVzc2lvbg0KbG4ubW9kZWwgPC0gbG0obXBnIH4gd3QgKyBocCwgZGF0YSA9IG10Y2FycykNCiMgZml0dGVkL3ByZWRpY3RlZCB2YWx1ZXMNCmZpdC52YWwgPC0gZml0dGVkKGxuLm1vZGVsKQ0KIyBNU0UNCk1TRSA9IG1lYW4oKGZpdC52YWwtbXRjYXJzJG1wZyleMikNCk1TRQ0KYGBgDQoNCiogKipSLXNxdWFyZWQqKjogQ29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiAtIGluZGljYXRlcyB0aGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBpbiB0aGUgdGFyZ2V0IHZhcmlhYmxlIGV4cGxhaW5lZCBieSB0aGUgbW9kZWwuDQoNCmBgYHtyfQ0KIyBmaXQgYSBsaW5lYXIgcmVncmVzc2lvbg0KbG4ubW9kZWwgPC0gbG0obXBnIH4gd3QgKyBocCwgZGF0YSA9IG10Y2FycykNCiMNCnN1bW1hcnkubW9kZWwgPSBzdW1tYXJ5KGxuLm1vZGVsKQ0KIyBSLnNxDQpyLnNxIDwtIHN1bW1hcnkubW9kZWwkci5zcXVhcmVkDQpjYmluZChSLnNxdWFyZWQgPSByLnNxKQ0KYGBgDQoNCiogKipBSUMgYW5kIFNCQyoqOiBUaGUgQWthaWtlIEluZm9ybWF0aW9uIENyaXRlcmlvbiAoQUlDKSBhbmQgU2Nod2FyeiBCYXllc2lhbiBDcml0ZXJpb24gKFNCQyksIGFsc28ga25vd24gYXMgdGhlIEJheWVzaWFuIEluZm9ybWF0aW9uIENyaXRlcmlvbiAoQklDKSwgYXJlIHdpZGVseSB1c2VkIG1ldHJpY3MgZm9yIG1vZGVsIHBlcmZvcm1hbmNlIGV2YWx1YXRpb24gaW4gc3RhdGlzdGljYWwgbW9kZWxpbmcuIFRoZXkgYXNzZXNzIHRoZSB0cmFkZS1vZmYgYmV0d2VlbiBtb2RlbCBmaXQgYW5kIGNvbXBsZXhpdHksIGhlbHBpbmcgaW4gbW9kZWwgc2VsZWN0aW9uLg0KDQpgYGB7cn0NCiMgZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24NCmxuLm1vZGVsIDwtIGxtKG1wZyB+IHd0ICsgaHAsIGRhdGEgPSBtdGNhcnMpDQojIENvbXB1dGUgQUlDIGFuZCBCSUMNCmFpYy52YWx1ZSA8LSBBSUMobG4ubW9kZWwpDQpiaWMudmFsdWUgPC0gQklDKGxuLm1vZGVsKQ0KY2JpbmQoQUlDPWFpYy52YWx1ZSwgU0JDID0gYmljLnZhbHVlKQ0KYGBgDQoNClIgZnVuY3Rpb25zIEFJQyBhbmQgQklDIGFyZSBnZW5lcmljIGZ1bmN0aW9ucyB0aGF0IGNhbiBiZSB1c2VkIGluIGBnbG0oKWAuIFRoZSBuZXh0IGV4YW1wbGUgc2hvd3MgaG93IHRvIGV4dHJhY3QgQUlDIGFuZCBTQkMgZnJvbSB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4NCg0KYGBge3J9DQojIEZpdCBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwNCm1vZGVsLmxvZ2l0IDwtIGdsbShhbSB+IHd0ICsgaHAsIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gbXRjYXJzKQ0KDQojIENvbXB1dGUgQUlDIGFuZCBCSUMNCmFpYy5sb2dpdCA8LSBBSUMobW9kZWwubG9naXQpDQpiaWMubG9naXQgPC0gQklDKG1vZGVsLmxvZ2l0KQ0KY2JpbmQoYWljLmxvZ2l0ID0gYWljLmxvZ2l0LCBiaWMubG9naXQgPSBiaWMubG9naXQpDQpgYGANCg0KDQoNCiMjIENhdGVnb3JpY2FsIFJlZ3Jlc3Npb24gKENsYXNzaWZpY2F0aW9uKSBNZXRyaWNzDQoNCldlIGZpcnN0IGludHJvZHVjZSBhIGZldyBjb25jZXB0cyBvZiB0aGUgY29uZnVzaW9uIG1hdHJpeCBhbmQgcmVsYXRlZCB0ZXJtcyBpbiBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgYmVmb3JlIGludHJvZHVjaW5nIHRoZSBwZXJmb3JtYW5jZSBtZXRyaWNzLg0KDQpBIGNvbmZ1c2lvbiBtYXRyaXggaXMgYSB0YWJsZSB1c2VkIHRvIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBhIGNsYXNzaWZpY2F0aW9uIG1vZGVsIGJ5IGNvbXBhcmluZyBpdHMgcHJlZGljdGlvbnMgd2l0aCB0aGUgYWN0dWFsIG91dGNvbWVzLiBJdCBwcm92aWRlcyBhIGRldGFpbGVkIGJyZWFrZG93biBvZiBob3cgd2VsbCB0aGUgbW9kZWwgZGlzdGluZ3Vpc2hlcyBiZXR3ZWVuIGRpZmZlcmVudCBjbGFzc2VzLiBUaGUgbWF0cml4IHR5cGljYWxseSBjb25zaXN0cyBvZiBmb3VyIGtleSBjb21wb25lbnRzOg0KDQoqKlRydWUgUG9zaXRpdmVzIChUUCkqKjogVGhlIG1vZGVsIGNvcnJlY3RseSBwcmVkaWN0ZWQgdGhlIHBvc2l0aXZlIGNsYXNzLg0KDQoqKlRydWUgTmVnYXRpdmVzIChUTikqKjogVGhlIG1vZGVsIGNvcnJlY3RseSBwcmVkaWN0ZWQgdGhlIG5lZ2F0aXZlIGNsYXNzLg0KDQoqKkZhbHNlIFBvc2l0aXZlcyAoRlApKio6IFRoZSBtb2RlbCBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgdGhlIHBvc2l0aXZlIGNsYXNzICh0eXBlIEkgZXJyb3IpLg0KDQoqKkZhbHNlIE5lZ2F0aXZlcyAoRk4pKio6IFRoZSBtb2RlbCBpbmNvcnJlY3RseSBwcmVkaWN0ZWQgdGhlIG5lZ2F0aXZlIGNsYXNzICh0eXBlIElJIGVycm9yKS4NCg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iOTAlIiwgZmlnLmNhcD0iSWxsdXN0cmF0aW9uIG9mIHRoZSBjb25mdXNpb24gbWF0cml4IGJhc2VkIG9uIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIG1vZGVsLiJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvQ29uZnVzaW9uTWF0cml4LmpwZyIpDQpgYGANCg0KDQoqICoqQWNjdXJhY3kqKjogVGhlIHJhdGlvIG9mIGNvcnJlY3RseSBwcmVkaWN0ZWQgaW5zdGFuY2VzIHRvIHRoZSB0b3RhbCBudW1iZXIgb2YgaW5zdGFuY2VzLiBXaGlsZSB3aWRlbHkgdXNlZCwgYWNjdXJhY3kgY2FuIGJlIG1pc2xlYWRpbmcgaW4gaW1iYWxhbmNlZCBkYXRhIHNldHMuDQoNCiogKipQcmVjaXNpb24qKjogUHJlY2lzaW9uIG1lYXN1cmVzIHRoZSBwcm9wb3J0aW9uIG9mIGNvcnJlY3RseSBwcmVkaWN0ZWQgcG9zaXRpdmUgaW5zdGFuY2VzLiANCg0KKiAqKlJlY2FsbCoqOiB3aGlsZSByZWNhbGwgY2FwdHVyZXMgdGhlIHByb3BvcnRpb24gb2YgYWN0dWFsIHBvc2l0aXZlcyBpZGVudGlmaWVkIGJ5IHRoZSBtb2RlbA0KDQoqICoqRjEtU2NvcmUqKjogVGhlIEYxLXNjb3JlIHByb3ZpZGVzIGEgaGFybW9uaWMgbWVhbiBvZiBwcmVjaXNpb24gYW5kIHJlY2FsbCwgYmFsYW5jaW5nIHRoZWlyIHRyYWRlLW9mZi4NCg0KJCQNCiBcdGV4dHtGMS1TY29yZSA9IDLDlyhQcmVjaXNpb27Dl1JlY2FsbCkvKFByZWNpc2lvbitSZWNhbGwpIH0NCiQkDQoNCiMjIExvZ2lzdGljIFJlZ3Jlc3Npb24gQ29uZnVzaW9uIE1hdHJpeA0KDQpEaWZmZXJlbnQgY29uZnVzaW9uIG1hdHJpY2VzIGZvciBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgYXJlIGVudGlyZWx5IGRlcGVuZGVudCBvbiB0aGUgY2hvaWNlIG9mIHRoZSB0aHJlc2hvbGQgcHJvYmFiaWxpdHkuIFRoZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHByZWRpY3RzIHByb2JhYmlsaXRpZXMgZm9yIHRoZSBwb3NpdGl2ZSBjbGFzcyAocmF0aGVyIHRoYW4gZGlyZWN0bHkgcHJlZGljdGluZyBjbGFzcyBsYWJlbHMpLCBhbmQgdGhlc2UgcHJvYmFiaWxpdGllcyBtdXN0IGJlIGNvbnZlcnRlZCBpbnRvIGNsYXNzIGxhYmVscyB1c2luZyBhIHRocmVzaG9sZC4gQnkgdmFyeWluZyB0aGlzIHRocmVzaG9sZCwgd2UgY2FuIGluZmx1ZW5jZSB0aGUgdHJhZGUtb2ZmIGJldHdlZW4gdGhlIG1vZGVsJ3Mgc2Vuc2l0aXZpdHkgKHJlY2FsbCkgYW5kIHNwZWNpZmljaXR5LiA8Zm9udCBjb2xvciA9ICJyZWQiPipcY29sb3J7cmVkfUluIG1hbnkgc29mdHdhcmUgcHJvZ3JhbXMgYW5kIGxpYnJhcmllcywgdGhlIGRlZmF1bHQgdGhyZXNob2xkIGlzIHNldCB0byAwLjUuKjwvZm9udD4NCg0KV2UgdXNlIHRoZSBgbXRjYXJzYCBkYXRhc2V0IGFuZCBmaXQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIGBhbWAgKHR5cGVzIG9mIHRyYW5zbWlzc2lvbjogbWFudWFsID0gMSwgYXV0b21hdGljID0gMCksIGB3dGAgKHdlaWdodCksIGFuZCBgaHBgIChob3JzZXBvd2VyKSBhcyBwcmVkaWN0b3JzLiBUaGUgcmVzdWx0aW5nIG1vZGVsIHdpbGwgcHJlZGljdCB0aGUgcHJvYmFiaWxpdHkgKCAkUChcdGV4dHthbX0gPSAxKSQgKS4gVXNpbmcgZml2ZSB0aHJlc2hvbGQgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgKDAuMCwgMC4yNSwgMC41LCAwLjc1LCAxLjApLCB3ZSBwcm9kdWNlIHRoZSBjb3JyZXNwb25kaW5nIGNvbmZ1c2lvbiBtYXRyaWNlcyBhcyBmb2xsb3dzLg0KDQpgYGB7cn0NCiMgbGlicmFyeShjYXJldCkNCiMgZml0IGEgbG9naXN0aWMNCm1vZGVsLmxvZ2l0IDwtIGdsbShhbSB+IHd0ICsgaHAsIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gbXRjYXJzKQ0KIyBwcmVkaWN0IHByb2JhYmlsaXR5IG9mIFAoWSA9ICJZZXMiKQ0KcHJvYmFiaWxpdGllcyA8LSByb3VuZChhcy52ZWN0b3IocHJlZGljdChtb2RlbC5sb2dpdCwgdHlwZSA9ICJyZXNwb25zZSIpKSwzKQ0KIw0KdGhyZXNob2xkcyA8LSBjKDAuMCwgMC4yNSwgMC41LCAwLjc1LCAxLjApDQoNCiMgTG9vcCB0aHJvdWdoIHRocmVzaG9sZHMgYW5kIGNyZWF0ZSBjb25mdXNpb24gbWF0cmljZXMNCmZvciAodGhyZXNob2xkIGluIHRocmVzaG9sZHMpIHsNCiAgY2F0KCJcbkNvbmZ1c2lvbiBNYXRyaXggZm9yIFRocmVzaG9sZCA9IiwgdGhyZXNob2xkLCAiXG4iKQ0KICANCiAgIyBDb252ZXJ0IHByb2JhYmlsaXRpZXMgdG8gcHJlZGljdGlvbnMNCiAgIyBhbTogMSA9IG1hbnVhbCB0cmFuc21pc3Npb24sIDAgPSBhdXRvbWF0aWMgdHJhbnNtaXNzaW9uDQogIHByZWRpY3Rpb25zIDwtIGlmZWxzZShwcm9iYWJpbGl0aWVzID4gdGhyZXNob2xkLCAiMSIsICIwIikNCiAgICAjIEdlbmVyYXRlIGNvbmZ1c2lvbiBtYXRyaXgNCiAgY20gPC0gY29uZnVzaW9uTWF0cml4KGFzLmZhY3RvcihwcmVkaWN0aW9ucyksIG10Y2FycyRhbSwgcG9zaXRpdmUgPSAiMSIpDQogIHByaW50KGNtJHRhYmxlKQ0KfQ0KDQpgYGANCg0KRm9yIGNvbnZlbmllbmNlLCB3ZSBhZGQgNSBhZGRpdGlvbmFsIGNvbHVtbnMgY29ycmVzcG9uZGluZyB0byB0aGUgcHJlZGljdGVkIGBhbWAgYmFzZWQgb24gNSB0aHJlc2hvbGRzOiAwLjAsIDAuMjUsIDAuNTAsIDAuNzUsIDEuMC4NCg0KYGBge3J9DQpwcmVkMC4wMCA8LSBpZmVsc2UocHJvYmFiaWxpdGllcyA+IDAsICIxIiwgIjAiKQ0KcHJlZDAuMjUgPC0gaWZlbHNlKHByb2JhYmlsaXRpZXMgPiAwLjI1LCAiMSIsICIwIikNCnByZWQwLjUwIDwtIGlmZWxzZShwcm9iYWJpbGl0aWVzID4gMC41MCwgIjEiLCAiMCIpDQpwcmVkMC43NSA8LSBpZmVsc2UocHJvYmFiaWxpdGllcyA+IDAuNzUsICIxIiwgIjAiKQ0KcHJlZDEuMDAgPC0gaWZlbHNlKHByb2JhYmlsaXRpZXMgPiAxLCAiMSIsICIwIikNCiMNClJlc3VsdHM9ZGF0YS5mcmFtZSh3dCA9bXRjYXJzJHd0LCANCiAgICAgICAgICAgICAgICAgICBocCA9IG10Y2FycyRocCwgDQogICAgICAgICAgICAgICAgICAgcHJlZCA9IHJvdW5kKHByb2JhYmlsaXRpZXMsMyksIA0KICAgICAgICAgICAgICAgICAgIGFtID0gbXRjYXJzJGFtLA0KICAgICAgICAgICAgICAgICAgIGFtLjAwID1wcmVkMC4wMCwNCiAgICAgICAgICAgICAgICAgICBhbS4yNSA9cHJlZDAuMjUsDQogICAgICAgICAgICAgICAgICAgYW0uNTAgPXByZWQwLjUwLA0KICAgICAgICAgICAgICAgICAgIGFtLjc1ID1wcmVkMC43NSwNCiAgICAgICAgICAgICAgICAgICBhbTEuMCA9cHJlZDEuMDANCiAgICAgICAgICAgICAgICAgICApDQojIyANClJlc3VsdHM9UmVzdWx0c1tvcmRlcihSZXN1bHRzJHByZWQpLF0NCiNwcmludChSZXN1bHRzKQ0KYGBgDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI3NSUiLCBmaWcuY2FwPSJBbiBpbGx1c3RyYXRpdmUgZXhhbXBsZSBvZiBjcmVhdGluZyBjb25mdXNpb24gbWF0cmljZXMgYmFzZWQgb24gYSBnaXZlbiBzZXQgb2YgdGhyZXNob2xkIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9Mb2dpdENvbmZ1c2lvbk1hdHJpeEV4YW1wbGUuanBnIikNCmBgYA0KDQojIyBST0MgQW5hbHlzaXMgYW5kIEFVQw0KDQpST0MgQW5hbHlzaXMgKFJlY2VpdmVyIE9wZXJhdGluZyBDaGFyYWN0ZXJpc3RpYyBBbmFseXNpcykgaXMgYSBncmFwaGljYWwgdGVjaG5pcXVlIHVzZWQgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIG1vZGVsLiBJdCBwbG90cyB0aGUgVHJ1ZSBQb3NpdGl2ZSBSYXRlIChUUFIpIChhbHNvIGNhbGxlZCBzZW5zaXRpdml0eSBvciByZWNhbGwpIGFnYWluc3QgdGhlIEZhbHNlIFBvc2l0aXZlIFJhdGUgKEZQUikgYXQgdmFyaW91cyBjbGFzc2lmaWNhdGlvbiB0aHJlc2hvbGRzLiANCg0KKgkqKlRydWUgUG9zaXRpdmUgUmF0ZSAoVFBSKSoqOiAkXGZyYWN7XHRleHR7VFB9fXtcdGV4dHtUUH0gKyBcdGV4dHtGTn19JCwgbWVhc3VyZXMgdGhlIG1vZGVsJ3MgYWJpbGl0eSB0byBjb3JyZWN0bHkgaWRlbnRpZnkgcG9zaXRpdmUgY2FzZXMuDQoNCioJKipGYWxzZSBQb3NpdGl2ZSBSYXRlIChGUFIpKio6ICRcZnJhY3tcdGV4dHtGUH19e1x0ZXh0e0ZQfSArIFx0ZXh0e1ROfX0kLCBtZWFzdXJlcyB0aGUgcHJvcG9ydGlvbiBvZiBuZWdhdGl2ZSBjYXNlcyBpbmNvcnJlY3RseSBjbGFzc2lmaWVkIGFzIHBvc2l0aXZlLg0KDQpUaGUgUk9DIGN1cnZlIGhlbHBzIHZpc3VhbGl6ZSB0aGUgdHJhZGUtb2ZmIGJldHdlZW4gc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5IGFjcm9zcyBkaWZmZXJlbnQgdGhyZXNob2xkcywgcHJvdmlkaW5nIGluc2lnaHQgaW50byB0aGUgbW9kZWwncyBjbGFzc2lmaWNhdGlvbiBjYXBhYmlsaXR5Lg0KDQpVc2luZyB0aGUgY29uZnVzaW9uIG1hdHJpY2VzIGFuZCB0aGUgZGVmaW5pdGlvbiBvZiBUUFIgYW5kIEZQUiBmcm9tIHRoZSBhYm92ZSBpbGx1c3RyYXRpdmUgZXhhbXBsZSB0byBwbG90IGFuIFJPQyBjdXJ2ZSBpbiB0aGUgZm9sbG93aW5nLg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9IlRoZSBleGFtcGxlIG9mIHRoZSBST0MgY3VydmUgb2YgYSBsb2dpc3RpYyByZWdyZXNzaW9uIGFzIGEgcHJlZGljdGl2ZSBtb2RlbC4ifQ0KIyB1c2luZyB0aGUgYWJvdmUgZGVmaW5pdGlvbiBvZiBUUFIgYW5kIEZQUg0KIyBUaGUgZmlyc3QgbnVtYmVyIGluIFRQUiBhbmQgRlBSIHJlcHJlc2VudGluZyB0aGUgdG9wIHJpZ2h0Lg0KIyBUaGlzIGlzIGJlY2F1c2UgYm90aCBUUFIgYW5kIEZQUiBhcmUgcHJvYmFiaWxpdGllcw0KVFBSID0gYygxLDEzLygxMyswKSwgMTMvKDEzKzApLCAxMi8oMTIrMSksIDExLygyKzExKSwgMC8oMTMrMCkpDQpGUFIgPSBjKDEsMy8oMTYrMyksIDEvKDE4KzEpLCAxLygxOCsxKSwgMS8oMSsxOCksIDAvKDEwKzApKQ0KcGxvdChGUFIsIFRQUiwgdHlwZSA9ICJiIiwgbWFpbiA9ICJBbiBJbGx1c3RyYXRpdmUgUk9DIEN1cnZlIiwgY29sID0iYmx1ZSIsDQogICAgIHhsYWI9IjEgLSBTcGVjaWZpdHkgKEZQUikiLCB5bGFiID0gIlNlbnNpdGl2aXR5IChUUFIpIikNCiMgYWRkIGEgb2ZmLWRpYWdvbmFsIHJlcHJlc2VudGluZyByYW5kb20gZ3Vlc3MgYWxnb3JpdGhtIGluIGJpbmFyeSBwcmVkaWN0aW9uDQphYmxpbmUoMCwxLCBsdHkgPSAyLCBjb2wgPSAicmVkIikNCiMgbGVnZW5kDQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYygiTG9naXN0aWMgTW9kZWwiLCAiUmFuZG9tIEd1ZXNzIiksDQogICAgICAgY29sPWMoImJsdWUiLCAicmVkIiksIGx0eSA9IDE6MiwgYnR5PSJuIiwgY2V4ID0gMC45KQ0KYGBgDQoNCg0KDQoNCioqQVVDIChBcmVhIFVuZGVyIHRoZSBDdXJ2ZSkqKjogVGhlIEFVQyBxdWFudGlmaWVzIHRoZSBvdmVyYWxsIHBlcmZvcm1hbmNlIG9mIHRoZSBST0MgY3VydmUgaW50byBhIHNpbmdsZSB2YWx1ZSwgcmFuZ2luZyBmcm9tIDAgdG8gMS4gVGhlcmUgaXMgbm8gZm9ybXVsYSB0byBjYWxjdWxhdGUgQVVDIHVubGVzcyB0aGVyZSBhcmUgc3BlY2lmaWVkIHBhcmFtZXRyaWMgZGlzdHJpYnV0aW9ucyBvZiAkUChZPTEpJCBhbmQgJFAoWT0wKSQgcmVzcGVjdGl2ZWx5LiBUaGUgY2FsY3VsYXRpb24gaXMgYmFzZWQgb24gdGhlIGFwcHJveGltYXRpb24gc2ltaWxhciB0byB0aGUgKipSaWVtYW5uIFN1bSoqIGFwcHJveGltYXRpb24gb2YgdGhlIGFyZWEgdW5kZXIgdGhlIGN1cnZlIGluIENhbGN1bHVzLiBXZSBpbGx1c3RyYXRlIHRoaXMgdXNpbmcgdGhlIGFib3ZlIFJPQyBjdXJ2ZS4NCg0KDQpgYGB7ciBmaWcuY2FwPSJJbGx1c3RyYXRpb24gb2YgYXBwcm94aW1hdGluZyB0aGUgYXJlYSBvZiB0aGUgUk9DIGN1cnZlLiJ9DQojIHVzaW5nIHRoZSBhYm92ZSBkZWZpbml0aW9uIG9mIFRQUiBhbmQgRlBSDQojIFRoZSBmaXJzdCBudW1iZXIgaW4gVFBSIGFuZCBGUFIgcmVwcmVzZW50cyB0aGUgdG9wIHJpZ2h0Lg0KIyBUaGlzIGlzIGJlY2F1c2UgYm90aCBUUFIgYW5kIEZQUiBhcmUgcHJvYmFiaWxpdGllcw0KVFBSID0gcm91bmQoYygxLDEzLygxMyswKSwgMTMvKDEzKzApLCAxMi8oMTIrMSksIDExLygyKzExKSwgMC8oMTMrMCkpLDMpDQpGUFIgPSByb3VuZChjKDEsMy8oMTYrMyksIDEvKDE4KzEpLCAxLygxOCsxKSwgMS8oMSsxOCksIDAvKDEwKzApKSwzKQ0KVFBSMCA9IFRQUls3OjFdDQpGUFIwID0gRlBSWzc6MV0NCiNwbG90KEZQUjAsIFRQUjAsIHR5cGUgPSAiYiIpDQpkYXRTZW5TcGUgPSBkYXRhLmZyYW1lKFRQUjAsIEZQUjApDQpnZ1JPQyA9IGdncGxvdChkYXRhID0gZGF0U2VuU3BlLCBhZXMoeCA9IEZQUjAsIHk9VFBSMCkpICsNCiAgICAgICAgZ2VvbV9saW5lKGNvbCA9ICJzdGVlbGJsdWUiKSArDQogICAgICAgIGdlb21fcG9pbnQoY29sID0gInJlZCIpICsNCiAgICAgICAgZ2VvbV9zZWdtZW50KHggPSBGUFIwLCB5ID0gMCwgeGVuZCA9IEZQUjAsIHllbmQgPSBUUFIwLCBjb2xvciA9IDQpICsNCiAgICAgICAgZ2VvbV9zZWdtZW50KHggPSAwLCB5ID0gMCwgeGVuZCA9IEZQUjBbN10sIHllbmQgPSAwLCBjb2xvciA9IDYpICsNCiAgICAgICAgZ2d0aXRsZSgiQXBwcm94aW1hdGluZyB0aGUgQVVDIG9mIExvZ2lzdGljIE1vZGVsIikgKw0KICAgICAgICB4bGFiKCIxLXNwZWNpZmljaXR5IChGUFIpIikgKyANCiAgICAgICAgeWxhYigiU2Vuc2l0aXZpdHkgKFRQUikiKSArDQogICAgICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDAuMDI1LCB5ID0gMC4xMjUsIGxhYmVsPSAiQSIpICsgDQogICAgICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDAuMTA1LCB5ID0gMC41LCBsYWJlbCA9ICJCIikgKw0KICAgICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjYwNSwgeSA9IDAuNSwgbGFiZWwgPSAiQyIpICsNCiAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjIpLA0KICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjE1LCAwLjE1LCAwLjc1LCAwLjE1KSwgImluY2hlcyIpLA0KICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDIsIGNvbG91ciA9ICJuYXZ5IiwgbGluZXR5cGU9MSkpDQojIHBhcnRpdGlvbiB0aGUgcmVnaW9uIHVuZGVyIHRoZSBST0MgaW50byB0cmFwZXpvaWRzDQpnZ3Bsb3RseShnZ1JPQykNCmBgYA0KDQpJbiB0aGlzIHBhcnRpY3VsYXIgZXhhbXBsZSwgdGhlIGVudGlyZSByZWdpb24gaXMgZGl2aWRlZCBpbnRvIHRocmVlIHN1Yi1yZWdpb25zOiBBICh0cmlhbmdsZSksIEIsIGFuZCBDIChib3RoIHJlY3RhbmdsZXMsIGluIGdlbmVyYWwsIHRyYXBlem9pZHMpLiBUaGUgdGhyZWUgYXJlYXMgYXJlOg0KDQoqICRBID0gKDAuMDUzXHRpbWVzIDEpIC8yID0gMC4wMjY1JA0KKiAkQiA9IFsoMC4xNTgtMC4wNTMpXHRpbWVzIDFdID0gMC4xMDUkDQoqICRDID0gWygxLTAuMTU4KVx0aW1lcyAxXSA9IDAuODQyJA0KDQpUaGUgQVVDIChvZiB0aGUgUk9DIGN1cnZlKSBpcyAkQVVDID0gMC4wMjY1ICsgMC4xMDUgKyAwLjg0MiA9IDAuOTczNSQuDQoNClRoZSBkZXNjcmlwdGlvbiBvZiBBVUMgaXMgb3V0bGluZWQgaW4gdGhlIGZvbGxvd2luZy4NCjx1bD4NCioJQW4gQVVDIG9mIDEuMCBpbmRpY2F0ZXMgcGVyZmVjdCBjbGFzc2lmaWNhdGlvbiAoaWRlYWwgbW9kZWwpLg0KDQoqCUFuIEFVQyBvZiAwLjUgc3VnZ2VzdHMgbm8gZGlzY3JpbWluYXRpdmUgYWJpbGl0eSAocmFuZG9tIGd1ZXNzaW5nKS4NCg0KKglBbiBBVUMgYmVsb3cgMC41IGltcGxpZXMgdGhlIG1vZGVsIHBlcmZvcm1zIHdvcnNlIHRoYW4gcmFuZG9tIGd1ZXNzaW5nLg0KPC91bD4NCg0KSW4gcHJhY3RpY2UsIGEgaGlnaGVyIEFVQyBpbmRpY2F0ZXMgYmV0dGVyIG1vZGVsIHBlcmZvcm1hbmNlLCBhcyBpdCByZWZsZWN0cyBhIGhpZ2hlciBwcm9iYWJpbGl0eSB0aGF0IHRoZSBjbGFzc2lmaWVyIHJhbmtzIGEgcmFuZG9tbHkgY2hvc2VuIHBvc2l0aXZlIGluc3RhbmNlIGhpZ2hlciB0aGFuIGEgcmFuZG9tbHkgY2hvc2VuIG5lZ2F0aXZlIGluc3RhbmNlLiANCg0KKipSZW1hcmsqKjogQm90aCBST0MgKGFzIGEgdmlzdWFsIGFpZCkgYW5kIEFVQyBhcmUgcHJpbWFyaWx5IHVzZWQgdG8gY29tcGFyZSB0aGUgcGVyZm9ybWFuY2Ugb2YgbXVsdGlwbGUgbW9kZWxzLg0KDQoNCioqUk9DIFdpdGggUiBMaWJyYXJpZXMqKg0KDQpTZXZlcmFsIFIgbGlicmFyaWVzIGhhdmUgZnVuY3Rpb25zIGZvciBST0MgYW5hbHlzaXMuIE9uZSBvZiB0aGUgZnJlcXVlbnRseSB1c2VkIG9uZXMgaXMgYHBST0NgLiBOZXh0LCB3ZSB1c2UgdGhlIFIgZnVuY3Rpb24gYHJvYygpYCBpbiB0aGUgUiBsaWJyYXJ5IGBwUk9DYCB0byBmaW5kIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eSBhbmQgdGhlIEFVQyB3ZSBjYWxjdWxhdGVkIGluIHRoZSBhYm92ZSBleGFtcGxlLg0KDQpgYGB7ciBmaWcuY2FwPSJJbGx1c3RyYXRpb24gb2YgYXBwcm94aW1hdGluZyB0aGUgYXJlYSBvZiB0aGUgUk9DIGN1cnZlIHVzaW5nIHBST0MgbGlicmFyeS4ifQ0KI2ZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NSwgDQptb2RlbC5sb2dpdCA8LSBnbG0oYW0gfiB3dCArIGhwLCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IG10Y2FycykNCiMgcHJlZGljdCBwcm9iYWJpbGl0eSBvZiBQKFkgPSAiWWVzIikNCnByb2JhYmlsaXRpZXMgPC0gcm91bmQoYXMudmVjdG9yKHByZWRpY3QobW9kZWwubG9naXQsIHR5cGUgPSAicmVzcG9uc2UiKSksMykNCiMjDQojICBjYXRlZ29yeSA9IEltcHV0ZWRGcmFtaW5naGFtJFRlblllYXJDSEQgPT0gMQ0KICBST0NvYmogPC0gcm9jKG10Y2FycyRhbSwgcHJvYmFiaWxpdGllcykNCiAgU2VuIDwtIFJPQ29iaiRzZW5zaXRpdml0aWVzDQogIFNwZSA8LSBST0NvYmokc3BlY2lmaWNpdGllcw0KICBwUk9DZGF0YSA8LSBkYXRhLmZyYW1lKFRQUj1TZW4sIEZQUiA9ICgxIC0gU3BlKSkNCiAgQVVDIDwtIFJPQ29iaiRhdWMNCmdncFJPQyA9IGdncGxvdChkYXRhID0gcFJPQ2RhdGEsIGFlcyh4ID0gRlBSLCB5PVRQUikpICsNCiAgICAgICAgZ2VvbV9saW5lKGNvbCA9ICJzdGVlbGJsdWUiKSArDQogICAgICAgIGdlb21fcG9pbnQoY29sID0gInJlZCIpICsNCiAgICAgICAgZ2d0aXRsZSgiUk9DIG9mIExvZ2lzdGljIE1vZGVsIFVzaW5nIHBST0MgTGlicmFyeSIpICsNCiAgICAgICAgeGxhYigiMS1zcGVjaWZpY2l0eSAoRlBSKSIpICsgDQogICAgICAgIHlsYWIoIlNlbnNpdGl2aXR5IChUUFIpIikgKw0KICAgICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjYwNSwgeSA9IDAuNSwgbGFiZWwgPSBhcyxjaGFyYWN0ZXIoQVVDKSkgKw0KICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjgsIDAuMiksDQogICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAuMTUsIDAuMTUsIDAuNzUsIDAuMTUpLCAiaW5jaGVzIiksDQogICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShzaXplID0gMiwgY29sb3VyID0gIm5hdnkiLCBsaW5ldHlwZT0xKSkNCiMgcGFydGl0aW9uIHRoZSByZWdpb24gdW5kZXIgdGhlIFJPQyBpbnRvIHRyYXBlem9pZHMNCmdncGxvdGx5KGdncFJPQykNCmBgYCAgDQoNCg0KXA0KDQojIyBDbHVzdGVyaW5nIE1ldHJpY3MNCg0KQ2x1c3RlcmluZyBtZXRyaWNzIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBjbHVzdGVyaW5nIGFsZ29yaXRobXMgYnkgbWVhc3VyaW5nIHRoZSBxdWFsaXR5IG9mIHRoZSByZXN1bHRpbmcgY2x1c3RlcnMuIFRoZXNlIG1ldHJpY3MgcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIGhvdyB3ZWxsIHRoZSBjbHVzdGVyaW5nIGFsZ29yaXRobSBoYXMgZ3JvdXBlZCB0aGUgZGF0YSBiYXNlZCBvbiBpdHMgc3RydWN0dXJlIG9yIHByZWRlZmluZWQgY3JpdGVyaWEuIFRoZXJlIGFyZSBkaWZmZXJlbnQgdHlwZXMgb2YgY2x1c3RlcmluZyBwZXJmb3JtYW5jZSBtZWFzdXJlcy4gV2UgaW50cm9kdWNlIG9ubHkgb25lIG1lYXN1cmUgYXMgYW4gZXhhbXBsZSBpbiB0aGUgZm9sbG93aW5nLg0KDQoqICoqU2lsaG91ZXR0ZSBTY29yZSoqOiBNZWFzdXJlcyBob3cgc2ltaWxhciBhbiBpbnN0YW5jZSBpcyB0byBpdHMgb3duIGNsdXN0ZXIgY29tcGFyZWQgdG8gb3RoZXIgY2x1c3RlcnMuIERlbm90ZSAkYShpKSQgaXMgdGhlIG1lYW4gZGlzdGFuY2UgdG8gb3RoZXIgcG9pbnRzIGluIHRoZSBzYW1lIGNsdXN0ZXIsIGFuZCAkYihpKSQgaXMgdGhlIG1lYW4gZGlzdGFuY2UgdG8gcG9pbnRzIGluIHRoZSBuZWFyZXN0IGNsdXN0ZXIuIFRoZSAqU2lsaG91ZXR0ZSBTY29yZSogaXMgZGVmaW5lZCBhcw0KDQokJA0KUyhpKSA9IFxmcmFje2IoaSkgLSBhKGkpfXtcbWF4IFx7YShpKSwgYihpKVx9fQ0KJCQNClwNCg0KDQojIEstRm9sZCBDcm9zcy1WYWxpZGF0aW9uDQoNCkNyb3NzLXZhbGlkYXRpb24gaXMgYSAqKmNvbXB1dGF0aW9uYWwgc3RhdGlzdGljYWwgdGVjaG5pcXVlKiogdXNlZCB0byBlc3RpbWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgYSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsIG9uIHVuc2VlbiBkYXRhLiBJdHMgZXNzZW5jZSBsaWVzIGluIGFzc2Vzc2luZyBob3cgd2VsbCBhIG1vZGVsIGdlbmVyYWxpemVzIHRvIGFuIGluZGVwZW5kZW50IGRhdGEgc2V0LCBlbnN1cmluZyB0aGF0IHRoZSBtb2RlbCBpc24ndCBvdmVyLWZpdHRlZCB0byB0aGUgdHJhaW5pbmcgZGF0YS4NCg0KQ3Jvc3MtdmFsaWRhdGlvbiBpbiBtYWNoaW5lIGxlYXJuaW5nIGFjdHMgYXMgYSBtZXRob2QgdG8gZW5zdXJlIG1vZGVsaW5nIGludGVncml0eSBieSBwcmV2ZW50aW5nIGEgbW9kZWwgZnJvbSBiZWluZyBib3RoIHRoZSAianVkZ2UiIChldmFsdWF0b3IpIGFuZCB0aGUgInBsYXllciIgKGxlYXJuZXIpIGF0IHRoZSBzYW1lIHRpbWUuIA0KDQoNCkJ5IGFwcGx5aW5nIGNyb3NzLXZhbGlkYXRpb24sIHByYWN0aXRpb25lcnMgZW5zdXJlIHRoYXQgbW9kZWxzIGFyZSByaWdvcm91c2x5IHRlc3RlZCBhbmQgcmVmaW5lZCwgbGVhZGluZyB0byBiZXR0ZXIgcmVhbC13b3JsZCBwZXJmb3JtYW5jZSBhbmQgcmVsaWFibGUgZGVjaXNpb24tbWFraW5nLiBBcyBkYXRhIHNldHMgZ3JvdyBhbmQgbW9kZWxzIGJlY29tZSBtb3JlIGNvbXBsZXgsIGNyb3NzLXZhbGlkYXRpb24gd2lsbCBjb250aW51ZSB0byBiZSBhIGNyaXRpY2FsIHRvb2wgaW4gdGhlIHB1cnN1aXQgb2YgcHJlZGljdGl2ZSBhY2N1cmFjeSBhbmQgcmVsaWFiaWxpdHkuDQoNCiMjIERhdGEgU3BsaXR0aW5nDQoNCkRhdGEgc3BsaXR0aW5nIGlzIHRoZSBwcm9jZXNzIG9mIGRpdmlkaW5nIGEgZGF0YSBzZXQgaW50byBzZXBhcmF0ZSBzdWJzZXRzIHRvIHRyYWluLCB2YWxpZGF0ZSwgYW5kIHRlc3QgYSBtYWNoaW5lIGxlYXJuaW5nIG1vZGVsLiBJdCBlbnN1cmVzIHRoYXQgYSBtb2RlbCBpcyB0cmFpbmVkIG9uIG9uZSBzdWJzZXQgYW5kIGV2YWx1YXRlZCBvbiBhbm90aGVyIHRvIGdlbmVyYWxpemUgd2VsbCB0byB1bnNlZW4gZGF0YS4gUHJvcGVyIGRhdGEgc3BsaXR0aW5nIGhlbHBzIHJlbGF4IHN0YXRpc3RpY2FsIGFzc3VtcHRpb25zLCBwcmV2ZW50cyBvdmVyZml0dGluZywgYW5kIGVuc3VyZXMgcm9idXN0IHBlcmZvcm1hbmNlIGV2YWx1YXRpb24uDQoNClR5cGljYWwgRGF0YSBTcGxpdHMgaW52b2x2ZSB0aHJlZSBzdWJzZXRzIGZvciBkaWZmZXJlbnQgcHVycG9zZXM6IFRyYWluaW5nLCB2YWxpZGF0aW9uLCBhbmQgdGVzdGluZyBzZXRzOg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iODAlIiwgZmlnLmNhcD0iVHlwaWNhbCB0aHJlZS13YXkgZGF0YSBzcGxpdHRpbmcgZm9yIHRyYWluaW5nLCB2YWxpZGF0aW5nLCBhbmQgdGVzdGluZy4ifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL0RhdGFTcGxpdHRpbmcuanBnIikNCmBgYA0KDQojIyMgVHJhaW5pbmcgU2V0DQoNCioqUHVycG9zZSoqOiBUaGUgdHJhaW5pbmcgc2V0IGlzIHVzZWQgdG8gdHJhaW4gdGhlIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgb3IgYWxnb3JpdGhtIGp1c3QgbGlrZSB0aGUgcmVndWxhciBtb2RlbCBmaXR0aW5nIHByb2Nlc3MgaW4gc3RhdGlzdGljcy4NCg0KKipTaXplKio6IFRoZSBzaXplIG9mIHRoZSB0cmFpbmluZyBkYXRhIGdlbmVyYWxseSBjb25zaXN0cyBvZiA2MC04MCUgb2YgdGhlIGVudGlyZSBkYXRhIHNldC4gT25lIA0KRGV0YWlsczogVGhlIG1vZGVsIGxlYXJucyBwYXR0ZXJucywgd2VpZ2h0cywgYW5kIHBhcmFtZXRlcnMgdXNpbmcgdGhpcyBzdWJzZXQuDQoNCiMjIyBWYWxpZGF0aW9uIFNldCAoT3B0aW9uYWwpDQoNClB1cnBvc2U6IFVzZWQgdG8gdHVuZSBoeXBlcnBhcmFtZXRlcnMgYW5kIGV2YWx1YXRlIG1vZGVsIHBlcmZvcm1hbmNlIGR1cmluZyB0cmFpbmluZy4NClNpemU6IFR5cGljYWxseSAxMC0yMCUgb2YgdGhlIGRhdGFzZXQuDQpEZXRhaWxzOiBIZWxwcyBpbiBtb2RlbCBzZWxlY3Rpb24gYW5kIGFzc2Vzc2luZyBwZXJmb3JtYW5jZSB3aXRob3V0IGJpYXMgZnJvbSB0aGUgdHJhaW5pbmcgZGF0YS4NCg0KIyMjIFRlc3QgU2V0DQoNClB1cnBvc2U6IFVzZWQgdG8gZXZhbHVhdGUgdGhlIGZpbmFsIG1vZGVsJ3MgcGVyZm9ybWFuY2Ugb24gdW5zZWVuIGRhdGEuDQpTaXplOiBUeXBpY2FsbHkgMTAtMjAlIG9mIHRoZSBkYXRhc2V0Lg0KRGV0YWlsczogUHJvdmlkZXMgYW4gdW5iaWFzZWQgZXZhbHVhdGlvbiBtZXRyaWMgZm9yIHRoZSBtb2RlbCdzIGdlbmVyYWxpemF0aW9uIGFiaWxpdHkuDQoNCg0KIyMgSy1Gb2xkIENyb3NzLVZhbGlkYXRpb24NCg0KSW4gay1mb2xkIGNyb3NzLXZhbGlkYXRpb24sIHRoZSBkYXRhIHNldCBpcyBkaXZpZGVkIGludG8gayBlcXVhbGx5IHNpemVkIHN1YnNldHMgb3IgZm9sZHMuIFRoZSBtb2RlbCBpcyB0cmFpbmVkIG9uIGstMSBmb2xkcyBhbmQgdGVzdGVkIG9uIHRoZSByZW1haW5pbmcgZm9sZHMuIFRoaXMgcHJvY2VzcyBpcyByZXBlYXRlZCBrIHRpbWVzLCB3aXRoIGVhY2ggZm9sZCBzZXJ2aW5nIGFzIHRoZSB0ZXN0IHNldCBvbmNlLiBUaGUgZmluYWwgcGVyZm9ybWFuY2UgbWV0cmljIGlzIGF2ZXJhZ2VkIG92ZXIgYWxsIGsgaXRlcmF0aW9ucywgcmVkdWNpbmcgYmlhcyBhbmQgdmFyaWFuY2UgaW4gdGhlIGV2YWx1YXRpb24uDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI4MCUiLCBmaWcuY2FwPSJHcmFwaGljYWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGNyb3NzLXZhbGlkYXRpb24gYWxnb3JpdGhtLiJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvQ1YtRXhwbGFuYXRpb24uanBnIikNCmBgYA0KDQoNCiMjIyBBbiBBbmFsb2d5DQoNCkEgKnJvdW5kLXJvYmluIHNwb3J0cyB0b3VybmFtZW50KiBzZXJ2ZXMgYXMgYSBkaXJlY3QgYW5hbG9neSB0byBjcm9zcy12YWxpZGF0aW9uIGluIG1hY2hpbmUgbGVhcm5pbmcsIHBhcnRpY3VsYXJseSBpbiBpdHMgc3RydWN0dXJlIGFuZCBnb2Fscy4NCg0KDQoqKjEuIFRlYW1zIGFzIERhdGEgU3Vic2V0cyAoRm9sZHMpKioNCg0KKiAqKlRvdXJuYW1lbnQqKjogRWFjaCB0ZWFtIGluIHRoZSByb3VuZC1yb2JpbiB0b3VybmFtZW50IHJlcHJlc2VudHMgYSBwYXJ0aWNpcGFudCB0aGF0IGNvbXBldGVzIHdpdGggZXZlcnkgb3RoZXIgdGVhbS4NCg0KKiAqKkNyb3NzLVZhbGlkYXRpb24qKjogVGhlIGRhdGFzZXQgaXMgc3BsaXQgaW50byBzdWJzZXRzIChvciBmb2xkcykuIEVhY2ggZm9sZCB0YWtlcyB0dXJucyBhY3RpbmcgYXMgdGhlICJ0ZWFtIiBiZWluZyB0ZXN0ZWQgd2hpbGUgdGhlIG90aGVycyBzZXJ2ZSBhcyAib3Bwb25lbnRzIiBmb3IgdHJhaW5pbmcuDQoNCg0KKioyLiBNYXRjaGVzIGFzIE1vZGVsIFRyYWluaW5nIGFuZCBWYWxpZGF0aW9uKioNCg0KKiAqKlRvdXJuYW1lbnQqKjogV2hlbiB0d28gdGVhbXMgcGxheSBhIG1hdGNoLCBvbmUgdGVhbSdzIHBlcmZvcm1hbmNlIGlzIGFzc2Vzc2VkIGJhc2VkIG9uIGl0cyBnYW1lcGxheSBhZ2FpbnN0IHRoZSBvdGhlci4NCg0KKiAqKkNyb3NzLVZhbGlkYXRpb24qKjogSW4gZWFjaCBpdGVyYXRpb24gb2YgY3Jvc3MtdmFsaWRhdGlvbiwgb25lIGZvbGQgKHRlYW0pIGlzIHRyZWF0ZWQgYXMgdGhlIHZhbGlkYXRpb24gc2V0IChiZWluZyBldmFsdWF0ZWQpLCBhbmQgdGhlIG90aGVycyBhcmUgY29tYmluZWQgdG8gZm9ybSB0aGUgdHJhaW5pbmcgc2V0IChvcHBvbmVudHMpLg0KDQoNCg0KKiozLiBSb3RhdGlvbmFsIEZhaXJuZXNzKioNCg0KKiAqKlRvdXJuYW1lbnQqKjogSW4gYSByb3VuZC1yb2JpbiB0b3VybmFtZW50LCBldmVyeSB0ZWFtIHBsYXlzIGFnYWluc3QgYWxsIG90aGVyIHRlYW1zLCBlbnN1cmluZyBmYWlybmVzcyBhbmQgZXF1YWwgb3Bwb3J0dW5pdHkgZm9yIGFzc2Vzc21lbnQuDQoNCiogKipDcm9zcy1WYWxpZGF0aW9uKio6IEVhY2ggZm9sZCBnZXRzIHRoZSBvcHBvcnR1bml0eSB0byBzZXJ2ZSBhcyB0aGUgdmFsaWRhdGlvbiBzZXQgZXhhY3RseSBvbmNlLCBlbnN1cmluZyB0aGF0IG5vIGRhdGEgaXMgb3Zlcmxvb2tlZCBkdXJpbmcgZXZhbHVhdGlvbi4NCg0KDQoNCioqNC4gT3ZlcmFsbCBQZXJmb3JtYW5jZSBhcyBhIE1lYXN1cmUgb2YgR2VuZXJhbGl6YXRpb24qKg0KDQoqICoqVG91cm5hbWVudCoqOiBBIHRlYW0ncyBvdmVyYWxsIHJhbmtpbmcgaXMgZGV0ZXJtaW5lZCBieSBpdHMgY3VtdWxhdGl2ZSBwZXJmb3JtYW5jZSBhY3Jvc3MgYWxsIG1hdGNoZXMsIG5vdCBqdXN0IGEgc2luZ2xlIGdhbWUuDQoNCiogKipDcm9zcy1WYWxpZGF0aW9uKio6IFRoZSBtb2RlbOKAmXMgb3ZlcmFsbCBwZXJmb3JtYW5jZSAoZS5nLiwgYXZlcmFnZSBhY2N1cmFjeSBvciBlcnJvciBhY3Jvc3MgZm9sZHMpIGlzIGNhbGN1bGF0ZWQgdG8gYXNzZXNzIGl0cyBhYmlsaXR5IHRvIGdlbmVyYWxpemUgYWNyb3NzIHRoZSBlbnRpcmUgZGF0YXNldCwgbm90IGp1c3Qgb24gb25lIHNwZWNpZmljIHNwbGl0Lg0KDQoNCg0KKio1LiBBdm9pZGluZyBPdmVyZml0dGluZyBhbmQgQmlhcyoqDQoNCiogKipUb3VybmFtZW50Kio6IElmIG9uZSB0ZWFtIHdpbnMgYnkgZm9jdXNpbmcgb24gYSBzaW5nbGUgb3Bwb25lbnTigJlzIHdlYWtuZXNzIGJ1dCBsb3NlcyB0byBvdGhlcnMsIGl0cyBzdHJhdGVneSBpc24ndCByb2J1c3QuIFJvdW5kLXJvYmluIGVuc3VyZXMgdGhhdCBlYWNoIHRlYW3igJlzIG92ZXJhbGwgc3RyYXRlZ3kgaXMgZXZhbHVhdGVkLg0KDQoqICoqQ3Jvc3MtVmFsaWRhdGlvbioqOiBCeSByb3RhdGluZyB2YWxpZGF0aW9uIHNldHMsIGNyb3NzLXZhbGlkYXRpb24gZW5zdXJlcyB0aGF0IHRoZSBtb2RlbCBpcyBub3Qgb3Zlcmx5IHR1bmVkIHRvIHNwZWNpZmljIHN1YnNldHMgb2YgdGhlIGRhdGEsIHJlZHVjaW5nIHRoZSByaXNrIG9mIG92ZXJmaXR0aW5nIGFuZCBwcm92aWRpbmcgYSBmYWlyIGV2YWx1YXRpb24uDQoNCg0KIyMjIEFuIEV4YW1wbGUNCg0KKioxLiBXb3JraW5nIERhdGEgU2V0IGFuZCBQcmFjdGljYWwgUXVlc3Rpb24qKg0KDQpEYXRhIGZyb20gbiA9IDExMyBob3NwaXRhbHMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgYXJlIHVzZWQgdG8gcHJlZGljdCB0aGUgcmlzayBvZiBhIGhvc3BpdGFsIHBhdGllbnQgYWNxdWlyaW5nIGFuIGluZmVjdGlvbiBkdXJpbmcgdGhlaXIgc3RheSwgYmFzZWQgb24gZmFjdG9ycyBpZGVudGlmaWVkIGJ5IGRvbWFpbiBleHBlcnRzLiBUaGUgZGF0YSBzZXQgaW5jbHVkZXMgMTEgdmFyaWFibGVzLiBOb3RlIHRoYXQgdGhpcyBpcyBub3QgYSBmb3JtYWwgZGF0YSBhbmFseXNpczsgd2Ugd2lsbCBub3QgcGVyZm9ybSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIChFREEpIG9yIGZlYXR1cmUgZW5naW5lZXJpbmcuIEJlbG93IGFyZSBhIGZldyB2YXJpYWJsZXMgdXNlZCBpbiB0aGlzIGlsbHVzdHJhdGl2ZSBleGFtcGxlLg0KDQoNCg0KKkluZmN0UnNrKjogaW5mZWN0aW9uIHJpc2sgKCR5JCkgDQoNCipTdGF5KjogYXZlcmFnZSBsZW5ndGggb2YgcGF0aWVudCBzdGF5ICgkeF8xJCkgDQoNCipBZ2UqOiBhdmVyYWdlIHBhdGllbnQgYWdlICgkeF8yJCkgDQoNCipYcmF5KjogdGhlIG1lYXN1cmUgb2YgaG93IG1hbnkgWC1yYXlzIGFyZSBnaXZlbiBpbiB0aGUgaG9zcGl0YWwgKCR4XzMkKQ0KDQpcDQoNCioqMi4gTW9kZWxzIHRvIEFkZHJlc3MgVGhlIFF1ZXN0aW9uKioNCg0KU2luY2UgdGhlIHByaW1hcnkgdXNlIG9mIGNyb3NzLXZhbGlkYXRpb24gaXMgdG8gY29tcGFyZSBwZXJmb3JtYW5jZSBhbW9uZyBkaWZmZXJlbnQgbW9kZWxzIGFuZCBhc3Npc3Qgb3B0aW1hbCBtb2RlbCBzZWxlY3Rpb24sIHdlIHdpbGwgYnVpbGQgdGhlIGZvbGxvd2luZyB0d28gbGluZWFyIG1vZGVscyBhbmQgdXNlIHRoZSA1LWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBtZXRob2QgdG8gaWRlbnRpZnkgdGhlIG9wdGltYWwgbW9kZWwuDQoNCiQkDQpcdGV4dHtNb2RlbCAxOiB9IFwgXCBcdGV4dHtJbmZjdFJza30gPSBcYWxwaGFfMCArIFxhbHBoYV8xXHRpbWVzIFx0ZXh0e1N0YXl9ICsgXGFscGhhXzIgXHRpbWVzIFx0ZXh0e0FnZX0gKyBcYWxwaGFfMyBcdGltZXMgXHRleHR7WHJheX0NCiQkDQoNCiQkDQpcdGV4dHtNb2RlbCAyOiB9IFwgXCBcdGV4dHtJbmZjdFJza30gPSBcYWxwaGFfMCArIFxhbHBoYV8xXHRpbWVzIFx0ZXh0e1N0YXl9ICsgXGFscGhhXzIgXHRpbWVzICBcdGV4dHtYcmF5fQ0KJCQNCg0KKiozLiBNb2RlbCBTZWxlY3Rpb24gdmlhIDUtZm9sZCBDcm9zcy1WYWxpZGF0aW9uKioNCg0KV2UgdXNlIDUtZm9sZCBjcm9zcy12YWxpZGF0aW9uIHRvIHNlbGVjdCB0aGUgYmV0dGVyIG9uZSBmcm9tIGBNb2RlbCAxYCBhbmQgYE1vZGVsIDJgIHdpdGhvdXQgdXNpbmcgYW55IGluZmVyZW50aWFsIHN0YXRpc3RpY3Mgc3VjaCBhcyB0ZXN0aW5nIHByb2NlZHVyZXMgb3IgbGlrZWxpaG9vZC1iYXNlZCBtZXRyaWNzIHN1Y2ggYXMgQUlDIG9yIFNCQyAoQklDKS4NCg0KKiAqRGF0YSBTcGxpdHRpbmcqOiBVc2luZyByYW5kb20gc3BsaXR0aW5nIHRvIHBhcnRpdGlvbiB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIHNldCAoMTAwKSBhbmQgdGVzdGluZyBzZXQgKDMxKS4NCg0KKiAqNS1mb2xkIGNyb3NzLXZhbGlkYXRpb24qOiBzcGxpdHRpbmcgdGhlIHRyYWluaW5nIHNldCBpbnRvIDUgZm9sZHMgd2l0aCBlcXVhbCBzaXplIHRvIHBlcmZvcm0gY3Jvc3MtdmFsaWRhdGlvbi4gDQoNCiogKkNob29zZSBQZXJmb3JtYW5jZSBNZWFzdXJlKjogVGhlIHJlc3VsdGluZyBtb2RlbCB3aWxsIGJlIHVzZWQgdG8gcHJlZGljdCB0aGUgaW5mZWN0aW9uIHJpc2suIFdlIHVzZSB0aGUgbWVhbiBzcXVhcmUgZXJyb3IgdG8gbWVhc3VyZSB0aGUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4NCg0KKiAqQ29tcHV0aW5nIGFuZCBDb2RpbmcqOiBXZSB3aWxsIGNvZGUgdGhlIENWIGFsZ29yaXRobSBkaXJlY3RseSB3aXRob3V0IHVzaW5nIGFueSBidWlsdC1pbiBmdW5jdGlvbiB3aXRoIENWIGNhcGFiaWxpdHkgdG8gZW5oYW5jZSB0ZWNobmljYWwgdW5kZXJzdGFuZGluZyBvZiB0aGUgY29uY2VwdC4gVGhlIGNvZGUgY2FuIGJlIGdlbmVyYWxpemVkIHRvIG90aGVyIG1vZGVscy4NCg0KDQpgYGB7ciBmaWcuY2FwPSJUaGUgbGluZSBwbG90IG9mIGZvbGRzIE1TRXMgZm9yIGNhbmRpZGF0ZSBtb2RlbHMuICJ9DQppbmZlY3Rpb24gPXJlYWQudGFibGUoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1Mi9kYXRzZXRzL2hvc3BpdGFsX2luZmN0LnR4dCIsIGhlYWRlciA9IFRSVUUpDQojIHVzaW5nIHNhbXBsZSgpIHRvIHBlcmZvcm0gcmFuZG9tIHNwbGl0dGluZw0KdHJhaW4uSUQgPSBzYW1wbGUoMTpkaW0oaW5mZWN0aW9uKVsxXSwgMTAwLCByZXBsYWNlID0gRkFMU0UpICAjIHdpdGhvdXQgcmVwbGFjZW1lbnQNCiMgdHJhaW5pbmcgc2V0DQp0cmFpbiA9IGluZmVjdGlvblt0cmFpbi5JRCxdDQp0ZXN0ID0gaW5mZWN0aW9uWy10cmFpbi5JRCxdDQojIyBzcGxpdHRpbmcgdGhlIHRyYWluIHNldCBpbnRvIDUgZm9sZHMgdG8gdHJhaW4gYW5kIHZhbGlkYXRlIHRoZSBjYW5kaWRhdGUgbW9kZWxzDQpOID0gZGltKHRyYWluKVsxXSAgICMgc2l6ZSBvZiB0cmFpbmluZyBkYXRhDQprID0gNSAgICAgICAgICAgICAgICMgbnVtYmVyIG9mIGZvbGRzDQpmbGQubiA9IGNlaWxpbmcoTi9rKQ0KTVNFLm0xID0gTlVMTCAgICAgICAjIG51bGwgdmVjdG9yIHRvIHN0b3JlIE1TRQ0KTVNFLm0yID0gTlVMTCAgICAgIA0KZm9yIChpIGluIDE6ayl7DQogIHZhbGlkLklEID0gKChpLTEpKmZsZC5uICsxKTooaSpmbGQubikgICMgb2JzZXJ2YXRpb24gSUQgZm9yIHRoZSBpLXRoIHZhbGlkYXRpb24gc2V0IA0KICB2YWxpZC5zZXQgPSB0cmFpblt2YWxpZC5JRCwgXQ0KICB0cmFpbi5zZXQgPSB0cmFpblstdmFsaWQuSUQsXQ0KICAjIyBmaXR0aW5nIHR3byBjYW5kaWRhdGUgbW9kZWxzIHdpdGggY29tYmluZWQgNCBmb2xkcyBvZiBkYXRhIHNldA0KICBNMDEgPSBsbShJbmZjdFJzayB+ICBTdGF5ICsgQWdlICsgWHJheSwgZGF0YSA9IHRyYWluLnNldCkNCiAgTTAyID0gbG0oSW5mY3RSc2sgfiAgU3RheSArIFhyYXksIGRhdGEgPSB0cmFpbi5zZXQpDQogICMjIFByZWRpY3RpbmcgSW5mZWN0UnNrIHVzaW5nIHRoZSB0d28gY2FuZGlkYXRlIG1vZGVscyBiYXNlZCBvbiB0aGUgdmFsaWRhdGUgc2V0DQogIHByZWRNMDEgPSBwcmVkaWN0KE0wMSwgbmV3ZGF0YSA9IHZhbGlkLnNldCkNCiAgcHJlZE0wMiA9IHByZWRpY3QoTTAyLCBuZXdkYXRhID0gdmFsaWQuc2V0KQ0KICAjIyBjYWxjdWxhdGluZyB0aGUgTVNFIGFzc29jaWF0ZWQgd2l0aCB0aGUgdHdvIG1vZGVscw0KICBNU0UubTFbaV0gPSBtZWFuKChwcmVkTTAxIC0gdmFsaWQuc2V0JEluZmN0UnNrKV4yKQ0KICBNU0UubTJbaV0gPSBtZWFuKChwcmVkTTAyIC0gdmFsaWQuc2V0JEluZmN0UnNrKV4yKQ0KfQ0KIyMgZGVmaW5lIGEgZGF0YSBmcmFtZSB0byBzdG9yZSB0aGUgTVNFIG9mIHRoZSBjYW5kaWRhdGUgbW9kZWxzDQojIyANCk1TRSA9IGRhdGEuZnJhbWUoZm9sZCA9IHJlcCgxOmssMiksIE1TRSA9IGMoTVNFLm0xLCBNU0UubTIpLCB0eXBlPWMocmVwKCJNb2RlbCAxIixrKSwgcmVwKCJNb2RlbCAyIiwgaykpKQ0KIyMgbGluZSBwbG90cyBvZiB0aGUgDQpjdnBsb3QgPSBnZ3Bsb3QoZGF0YSA9IE1TRSwgYWVzKHg9Zm9sZCwgeT1NU0UsIGNvbG9yID0gdHlwZSkpICsNCiAgICAgICAgIGdlb21fbGluZSgpICsNCiAgICAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNiksDQogICAgICAgICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwyKSkgKw0KICAgICAgICAgZ2VvbV90ZXh0KG1hcHBpbmcgPSBhZXMoeD0xLjAsIHk9MC4yNSwgDQogICAgICAgICAgICAgICAgICBsYWJlbD1wYXN0ZSgiTW9kZWwgMSBNZWFuIE1TRTogPSAiLCByb3VuZChtZWFuKE1TRS5tMSksMyksICIiKSksIA0KICAgICAgICAgICAgICAgICAgIGhqdXN0PTApICsNCiAgICAgICAgIGdlb21fdGV4dChtYXBwaW5nID0gYWVzKHg9MS4wLCB5PTAuMTUsIA0KICAgICAgICAgICAgICAgICAgbGFiZWw9cGFzdGUoIk1vZGVsIDIgTWVhbiBNU0U6ID0gIiwgcm91bmQobWVhbihNU0UubTIpLDMpLCAiIikpLCANCiAgICAgICAgICAgICAgICAgICBoanVzdD0wKSArIA0KICAgICAgICAgZ2d0aXRsZSgiTGluZSBwbG90cyBvZiBNU0UgY2FuZGlkYXRlIE1vZGVscyBhY3Jvc3MgZm9sZHMiKSArDQogICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsMSwxLDEpLCAiY20iKSkNCmdncGxvdGx5KGN2cGxvdCkNCmBgYA0KDQoqICpSZXBvcnRpbmcgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBmaW5hbCBvcHRpbWFsIG1vZGVsKjogcHJlZGljdCBmaW5kIE1TRSBiYXNlZCBvbiB0aGUgaG9sZC11cCB0ZXN0IHNldC4gVGhpcyBNU0UgaXMgc3VwcG9zZWQgdG8gYmUgc2ltaWxhciB0byB0aGUgYWN0dWFsIE1TRSB3aGVuIGltcGxlbWVudGluZyB0aGUgZmluYWwgbW9kZWwgdG8gdW5zZWVuIGZ1dHVyZSBkYXRhIGluIHR3byBzdGVwcy4NCiAgKyBGaXQgdGhlIGZpbmFsIG1vZGVsIHRvIHRoZSBlbnRpcmUgdHJhaW4gc2V0Lg0KICArIFVzZSB0aGUgZml0dGVkIGZpbmFsIG1vZGVsIChpLmUuLCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhcmUgYmFzZWQgb24gdGhlIGVudGlyZSB0cmFpbiBzZXQpIHRvIHByZWRpY3QgYEluZmVjdFJza2ANCiAgKyBmaW5kIHRoZSBNU0UgdXNpbmcgdGhlIHByZWRpY3RlZCBgSW5mZWN0UnNrYCBhbmQgY29ycmVzcG9uZGluZyB0cnVlIGBJbmZlY1Jza2AgdG8gcmVwb3J0Lg0KICANCmBgYHtyIGZpZy5jYXA9IlRoZSBsaW5lIHBsb3Qgb2YgZm9sZHMgTVNFcyBmb3IgY2FuZGlkYXRlIG1vZGVscyBhcyB3ZWxsIGFzIHRoZSB0ZXN0aW5nIE1TRSBvZiB0aGUgZmluYWwgbW9kZWwuICJ9DQppZiAobWVhbihNU0UubTEpIDwgbWVhbihNU0UubTIpKXsNCiAgbW9kZWwudGVzdCA9IGxtKEluZmN0UnNrIH4gIFN0YXkgKyBBZ2UgKyBYcmF5LCBkYXRhID0gdHJhaW4pDQogIHByZWQubW9kZWwudGVzdCA9IHByZWRpY3QobW9kZWwudGVzdCwgbmV3ZGF0YSA9IHRlc3QpDQogIHRlc3QuTVNFID0gcm91bmQobWVhbigocHJlZC5tb2RlbC50ZXN0LXRlc3QkSW5mY3RSc2spXjIpLDMpDQogIGZpbmFsLnRlc3QucGxvdCA9IGN2cGxvdCArDQogICAgICAgZ2VvbV90ZXh0KG1hcHBpbmcgPSBhZXMoeD01LjAsIHk9MC4yLA0KICAgICAgICAgICAgICAgICAgbGFiZWw9cGFzdGUoIkZpbmFsIE1vZGVsIFRlc3QgTVNFOiA9ICIsIHJvdW5kKG1lYW4odGVzdC5NU0UpLDMpLCAiIikpLCANCiAgICAgICAgICAgICAgICAgICBoanVzdD0wLCApIA0KICBnZ3Bsb3RseShmaW5hbC50ZXN0LnBsb3QpDQp9IGVsc2V7DQogIG1vZGVsLnRlc3QgPSBsbShJbmZjdFJzayB+ICBTdGF5ICsgWHJheSwgZGF0YSA9IHRyYWluKQ0KICBwcmVkLm1vZGVsLnRlc3QgPSBwcmVkaWN0KG1vZGVsLnRlc3QsIG5ld2RhdGEgPSB0ZXN0KQ0KICB0ZXN0Lk1TRSA9IHJvdW5kKG1lYW4oKHByZWQubW9kZWwudGVzdC10ZXN0JEluZmN0UnNrKV4yKSwzKQ0KICBmaW5hbC50ZXN0LnBsb3QgPSBjdnBsb3QgKw0KICAgICAgIGdlb21fdGV4dChtYXBwaW5nID0gYWVzKHg9NS4wLCB5PTAuMiwNCiAgICAgICAgICAgICAgICAgIGxhYmVsPXBhc3RlKCJGaW5hbCBNb2RlbCBUZXN0IE1TRTogPSAiLCByb3VuZChtZWFuKHRlc3QuTVNFKSwzKSwgIiIpKSwgDQogICAgICAgICAgICAgICAgICAgaGp1c3Q9MCkNCiAgZ2dwbG90bHkoZmluYWwudGVzdC5wbG90KQ0KfQ0KYGBgDQoNCg0KDQoNCiogKkhhbmRpbmcgdGhlIGZpbmFsIG1vZGVsIGZvciBpbXBsZW1lbnRhdGlvbi9kZXBsb3ltZW50KjogVGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIG9mIHRoZSBmaW5hbCB3b3JraW5nIG1vZGVsIGZvciBpbXBsZW1lbnRhdGlvbi9kZXBsb3ltZW50IHNob3VsZCBiZSB1cGRhdGVkIGJhc2VkIG9uIHRoZSBlbnRpcmUgZGF0YSBzZXQgKGkuZS4sIGNvbWJpbmUgdGhlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMpLg0KDQoNCg0KDQoNCg0K